mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'master' into fix/35-isActive-liquidations
This commit is contained in:
commit
fb15afda8e
|
|
@ -2,6 +2,7 @@ import {usePlugin, BuidlerConfig} from '@nomiclabs/buidler/config';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import {accounts} from './test-wallets.js';
|
import {accounts} from './test-wallets.js';
|
||||||
import {eEthereumNetwork} from './helpers/types';
|
import {eEthereumNetwork} from './helpers/types';
|
||||||
|
import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/constants';
|
||||||
|
|
||||||
usePlugin('@nomiclabs/buidler-ethers');
|
usePlugin('@nomiclabs/buidler-ethers');
|
||||||
usePlugin('buidler-typechain');
|
usePlugin('buidler-typechain');
|
||||||
|
|
@ -59,6 +60,7 @@ const config: any = {
|
||||||
networks: {
|
networks: {
|
||||||
coverage: {
|
coverage: {
|
||||||
url: 'http://localhost:8555',
|
url: 'http://localhost:8555',
|
||||||
|
chainId: COVERAGE_CHAINID,
|
||||||
},
|
},
|
||||||
kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42),
|
kovan: getCommonNetworkConfig(eEthereumNetwork.kovan, 42),
|
||||||
ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3),
|
ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3),
|
||||||
|
|
@ -68,7 +70,7 @@ const config: any = {
|
||||||
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
|
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
|
||||||
gas: DEFAULT_BLOCK_GAS_LIMIT,
|
gas: DEFAULT_BLOCK_GAS_LIMIT,
|
||||||
gasPrice: 8000000000,
|
gasPrice: 8000000000,
|
||||||
chainId: 31337,
|
chainId: BUIDLEREVM_CHAINID,
|
||||||
throwOnTransactionFailures: true,
|
throwOnTransactionFailures: true,
|
||||||
throwOnCallFailures: true,
|
throwOnCallFailures: true,
|
||||||
accounts: accounts.map(({secretKey, balance}: {secretKey: string; balance: string}) => ({
|
accounts: accounts.map(({secretKey, balance}: {secretKey: string; balance: string}) => ({
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,13 @@ interface ILendingPool {
|
||||||
**/
|
**/
|
||||||
event Withdraw(address indexed reserve, address indexed user, uint256 amount);
|
event Withdraw(address indexed reserve, address indexed user, uint256 amount);
|
||||||
|
|
||||||
|
event BorrowAllowanceDelegated(
|
||||||
|
address indexed asset,
|
||||||
|
address indexed fromUser,
|
||||||
|
address indexed toUser,
|
||||||
|
uint256 interestRateMode,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* @dev emitted on borrow
|
* @dev emitted on borrow
|
||||||
* @param reserve the address of the reserve
|
* @param reserve the address of the reserve
|
||||||
|
|
@ -40,7 +47,8 @@ interface ILendingPool {
|
||||||
**/
|
**/
|
||||||
event Borrow(
|
event Borrow(
|
||||||
address indexed reserve,
|
address indexed reserve,
|
||||||
address indexed user,
|
address user,
|
||||||
|
address indexed onBehalfOf,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 borrowRateMode,
|
uint256 borrowRateMode,
|
||||||
uint256 borrowRate,
|
uint256 borrowRate,
|
||||||
|
|
@ -151,6 +159,27 @@ interface ILendingPool {
|
||||||
**/
|
**/
|
||||||
function withdraw(address reserve, uint256 amount) external;
|
function withdraw(address reserve, uint256 amount) external;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
|
||||||
|
* @param asset The underlying asset of the debt token
|
||||||
|
* @param user The user to give allowance to
|
||||||
|
* @param interestRateMode Type of debt: 1 for stable, 2 for variable
|
||||||
|
* @param amount Allowance amount to borrow
|
||||||
|
**/
|
||||||
|
function delegateBorrowAllowance(
|
||||||
|
address asset,
|
||||||
|
address user,
|
||||||
|
uint256 interestRateMode,
|
||||||
|
uint256 amount
|
||||||
|
) external;
|
||||||
|
|
||||||
|
function getBorrowAllowance(
|
||||||
|
address fromUser,
|
||||||
|
address toUser,
|
||||||
|
address asset,
|
||||||
|
uint256 interestRateMode
|
||||||
|
) external view returns (uint256);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
|
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
|
||||||
* already deposited enough collateral.
|
* already deposited enough collateral.
|
||||||
|
|
@ -162,7 +191,8 @@ interface ILendingPool {
|
||||||
address reserve,
|
address reserve,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 interestRateMode,
|
uint256 interestRateMode,
|
||||||
uint16 referralCode
|
uint16 referralCode,
|
||||||
|
address onBehalfOf
|
||||||
) external;
|
) external;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
|
|
||||||
mapping(address => ReserveLogic.ReserveData) internal _reserves;
|
mapping(address => ReserveLogic.ReserveData) internal _reserves;
|
||||||
mapping(address => UserConfiguration.Map) internal _usersConfig;
|
mapping(address => UserConfiguration.Map) internal _usersConfig;
|
||||||
|
// debt token address => user who gives allowance => user who receives allowance => amount
|
||||||
|
mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance;
|
||||||
|
|
||||||
address[] internal _reservesList;
|
address[] internal _reservesList;
|
||||||
|
|
||||||
|
|
@ -105,7 +107,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
|
|
||||||
bool isFirstDeposit = IAToken(aToken).balanceOf(onBehalfOf) == 0;
|
bool isFirstDeposit = IAToken(aToken).balanceOf(onBehalfOf) == 0;
|
||||||
if (isFirstDeposit) {
|
if (isFirstDeposit) {
|
||||||
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.index, true);
|
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
|
IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
|
||||||
|
|
@ -151,7 +153,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
|
reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
|
||||||
|
|
||||||
if (amountToWithdraw == userBalance) {
|
if (amountToWithdraw == userBalance) {
|
||||||
_usersConfig[msg.sender].setUsingAsCollateral(reserve.index, false);
|
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex);
|
IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex);
|
||||||
|
|
@ -159,6 +161,35 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
emit Withdraw(asset, msg.sender, amount);
|
emit Withdraw(asset, msg.sender, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBorrowAllowance(
|
||||||
|
address fromUser,
|
||||||
|
address toUser,
|
||||||
|
address asset,
|
||||||
|
uint256 interestRateMode
|
||||||
|
) external override view returns (uint256) {
|
||||||
|
return
|
||||||
|
_borrowAllowance[_reserves[asset].getDebtTokenAddress(interestRateMode)][fromUser][toUser];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
|
||||||
|
* @param asset The underlying asset of the debt token
|
||||||
|
* @param user The user to give allowance to
|
||||||
|
* @param interestRateMode Type of debt: 1 for stable, 2 for variable
|
||||||
|
* @param amount Allowance amount to borrow
|
||||||
|
**/
|
||||||
|
function delegateBorrowAllowance(
|
||||||
|
address asset,
|
||||||
|
address user,
|
||||||
|
uint256 interestRateMode,
|
||||||
|
uint256 amount
|
||||||
|
) external override {
|
||||||
|
address debtToken = _reserves[asset].getDebtTokenAddress(interestRateMode);
|
||||||
|
|
||||||
|
_borrowAllowance[debtToken][msg.sender][user] = amount;
|
||||||
|
emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
|
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
|
||||||
* already deposited enough collateral.
|
* already deposited enough collateral.
|
||||||
|
|
@ -166,20 +197,34 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
* @param amount the amount to be borrowed
|
* @param amount the amount to be borrowed
|
||||||
* @param interestRateMode the interest rate mode at which the user wants to borrow. Can be 0 (STABLE) or 1 (VARIABLE)
|
* @param interestRateMode the interest rate mode at which the user wants to borrow. Can be 0 (STABLE) or 1 (VARIABLE)
|
||||||
* @param referralCode a referral code for integrators
|
* @param referralCode a referral code for integrators
|
||||||
|
* @param onBehalfOf address of the user who will receive the debt
|
||||||
**/
|
**/
|
||||||
function borrow(
|
function borrow(
|
||||||
address asset,
|
address asset,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 interestRateMode,
|
uint256 interestRateMode,
|
||||||
uint16 referralCode
|
uint16 referralCode,
|
||||||
|
address onBehalfOf
|
||||||
) external override {
|
) external override {
|
||||||
|
ReserveLogic.ReserveData storage reserve = _reserves[asset];
|
||||||
|
|
||||||
|
if (onBehalfOf != msg.sender) {
|
||||||
|
address debtToken = reserve.getDebtTokenAddress(interestRateMode);
|
||||||
|
|
||||||
|
_borrowAllowance[debtToken][onBehalfOf][msg
|
||||||
|
.sender] = _borrowAllowance[debtToken][onBehalfOf][msg.sender].sub(
|
||||||
|
amount,
|
||||||
|
Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH
|
||||||
|
);
|
||||||
|
}
|
||||||
_executeBorrow(
|
_executeBorrow(
|
||||||
ExecuteBorrowParams(
|
ExecuteBorrowParams(
|
||||||
asset,
|
asset,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
|
onBehalfOf,
|
||||||
amount,
|
amount,
|
||||||
interestRateMode,
|
interestRateMode,
|
||||||
_reserves[asset].aTokenAddress,
|
reserve.aTokenAddress,
|
||||||
referralCode,
|
referralCode,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
@ -247,7 +292,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
|
reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
|
||||||
|
|
||||||
if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
|
if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
|
||||||
_usersConfig[onBehalfOf].setBorrowing(reserve.index, false);
|
_usersConfig[onBehalfOf].setBorrowing(reserve.id, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
IERC20(asset).safeTransferFrom(user, aToken, paybackAmount);
|
IERC20(asset).safeTransferFrom(user, aToken, paybackAmount);
|
||||||
|
|
@ -360,7 +405,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
_addressesProvider.getPriceOracle()
|
_addressesProvider.getPriceOracle()
|
||||||
);
|
);
|
||||||
|
|
||||||
_usersConfig[msg.sender].setUsingAsCollateral(reserve.index, useAsCollateral);
|
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral);
|
||||||
|
|
||||||
if (useAsCollateral) {
|
if (useAsCollateral) {
|
||||||
emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
|
emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
|
||||||
|
|
@ -524,6 +569,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
ExecuteBorrowParams(
|
ExecuteBorrowParams(
|
||||||
asset,
|
asset,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
|
msg.sender,
|
||||||
vars.amountPlusPremium.sub(vars.availableBalance),
|
vars.amountPlusPremium.sub(vars.availableBalance),
|
||||||
mode,
|
mode,
|
||||||
vars.aTokenAddress,
|
vars.aTokenAddress,
|
||||||
|
|
@ -617,7 +663,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
reserve.currentStableBorrowRate,
|
reserve.currentStableBorrowRate,
|
||||||
IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(),
|
IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(),
|
||||||
reserve.liquidityIndex,
|
reserve.liquidityIndex,
|
||||||
reserve.lastVariableBorrowIndex,
|
reserve.variableBorrowIndex,
|
||||||
reserve.lastUpdateTimestamp
|
reserve.lastUpdateTimestamp
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -683,7 +729,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
stableRateLastUpdated = IStableDebtToken(reserve.stableDebtTokenAddress).getUserLastUpdated(
|
stableRateLastUpdated = IStableDebtToken(reserve.stableDebtTokenAddress).getUserLastUpdated(
|
||||||
user
|
user
|
||||||
);
|
);
|
||||||
usageAsCollateralEnabled = _usersConfig[user].isUsingAsCollateral(reserve.index);
|
usageAsCollateralEnabled = _usersConfig[user].isUsingAsCollateral(reserve.id);
|
||||||
variableBorrowIndex = IVariableDebtToken(reserve.variableDebtTokenAddress).getUserIndex(user);
|
variableBorrowIndex = IVariableDebtToken(reserve.variableDebtTokenAddress).getUserIndex(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -753,6 +799,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
struct ExecuteBorrowParams {
|
struct ExecuteBorrowParams {
|
||||||
address asset;
|
address asset;
|
||||||
address user;
|
address user;
|
||||||
|
address onBehalfOf;
|
||||||
uint256 amount;
|
uint256 amount;
|
||||||
uint256 interestRateMode;
|
uint256 interestRateMode;
|
||||||
address aTokenAddress;
|
address aTokenAddress;
|
||||||
|
|
@ -766,7 +813,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
**/
|
**/
|
||||||
function _executeBorrow(ExecuteBorrowParams memory vars) internal {
|
function _executeBorrow(ExecuteBorrowParams memory vars) internal {
|
||||||
ReserveLogic.ReserveData storage reserve = _reserves[vars.asset];
|
ReserveLogic.ReserveData storage reserve = _reserves[vars.asset];
|
||||||
UserConfiguration.Map storage userConfig = _usersConfig[msg.sender];
|
UserConfiguration.Map storage userConfig = _usersConfig[vars.onBehalfOf];
|
||||||
|
|
||||||
address oracle = _addressesProvider.getPriceOracle();
|
address oracle = _addressesProvider.getPriceOracle();
|
||||||
|
|
||||||
|
|
@ -776,7 +823,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
|
|
||||||
ValidationLogic.validateBorrow(
|
ValidationLogic.validateBorrow(
|
||||||
reserve,
|
reserve,
|
||||||
vars.asset,
|
vars.onBehalfOf,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
amountInETH,
|
amountInETH,
|
||||||
vars.interestRateMode,
|
vars.interestRateMode,
|
||||||
|
|
@ -787,7 +834,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
oracle
|
oracle
|
||||||
);
|
);
|
||||||
|
|
||||||
uint256 reserveIndex = reserve.index;
|
uint256 reserveIndex = reserve.id;
|
||||||
if (!userConfig.isBorrowing(reserveIndex)) {
|
if (!userConfig.isBorrowing(reserveIndex)) {
|
||||||
userConfig.setBorrowing(reserveIndex, true);
|
userConfig.setBorrowing(reserveIndex, true);
|
||||||
}
|
}
|
||||||
|
|
@ -803,12 +850,12 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
currentStableRate = reserve.currentStableBorrowRate;
|
currentStableRate = reserve.currentStableBorrowRate;
|
||||||
|
|
||||||
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
||||||
vars.user,
|
vars.onBehalfOf,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
currentStableRate
|
currentStableRate
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.user, vars.amount);
|
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.onBehalfOf, vars.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve.updateInterestRates(
|
reserve.updateInterestRates(
|
||||||
|
|
@ -819,12 +866,13 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (vars.releaseUnderlying) {
|
if (vars.releaseUnderlying) {
|
||||||
IAToken(vars.aTokenAddress).transferUnderlyingTo(msg.sender, vars.amount);
|
IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Borrow(
|
emit Borrow(
|
||||||
vars.asset,
|
vars.asset,
|
||||||
msg.sender,
|
vars.user,
|
||||||
|
vars.onBehalfOf,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
vars.interestRateMode,
|
vars.interestRateMode,
|
||||||
ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
|
ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
|
||||||
|
|
@ -844,7 +892,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
reserveAlreadyAdded = true;
|
reserveAlreadyAdded = true;
|
||||||
}
|
}
|
||||||
if (!reserveAlreadyAdded) {
|
if (!reserveAlreadyAdded) {
|
||||||
_reserves[asset].index = uint8(_reservesList.length);
|
_reserves[asset].id = uint8(_reservesList.length);
|
||||||
_reservesList.push(asset);
|
_reservesList.push(asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
|
||||||
|
|
||||||
mapping(address => ReserveLogic.ReserveData) internal reserves;
|
mapping(address => ReserveLogic.ReserveData) internal reserves;
|
||||||
mapping(address => UserConfiguration.Map) internal usersConfig;
|
mapping(address => UserConfiguration.Map) internal usersConfig;
|
||||||
|
mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance;
|
||||||
|
|
||||||
address[] internal reservesList;
|
address[] internal reservesList;
|
||||||
|
|
||||||
|
|
@ -358,7 +359,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
|
||||||
vars.collateralAtoken.burn(user, receiver, vars.maxCollateralToLiquidate, collateralReserve.liquidityIndex);
|
vars.collateralAtoken.burn(user, receiver, vars.maxCollateralToLiquidate, collateralReserve.liquidityIndex);
|
||||||
|
|
||||||
if (vars.userCollateralBalance == vars.maxCollateralToLiquidate) {
|
if (vars.userCollateralBalance == vars.maxCollateralToLiquidate) {
|
||||||
usersConfig[user].setUsingAsCollateral(collateralReserve.index, false);
|
usersConfig[user].setUsingAsCollateral(collateralReserve.id, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.principalAToken = debtReserve.aTokenAddress;
|
vars.principalAToken = debtReserve.aTokenAddress;
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,14 @@ library Errors {
|
||||||
string public constant INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
|
string public constant INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
|
||||||
string public constant CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The actual balance of the protocol is inconsistent'
|
string public constant CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The actual balance of the protocol is inconsistent'
|
||||||
string public constant INVALID_FLASHLOAN_MODE = '43'; //Invalid flashloan mode selected
|
string public constant INVALID_FLASHLOAN_MODE = '43'; //Invalid flashloan mode selected
|
||||||
|
string public constant BORROW_ALLOWANCE_ARE_NOT_ENOUGH = '54'; // User borrows on behalf, but allowance are too small
|
||||||
string public constant REENTRANCY_NOT_ALLOWED = '52';
|
string public constant REENTRANCY_NOT_ALLOWED = '52';
|
||||||
string public constant FAILED_REPAY_WITH_COLLATERAL = '53';
|
string public constant FAILED_REPAY_WITH_COLLATERAL = '53';
|
||||||
|
|
||||||
// require error messages - aToken
|
// require error messages - aToken
|
||||||
string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'
|
string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'
|
||||||
string public constant INTEREST_REDIRECTION_NOT_ALLOWED = '29'; // 'Caller is not allowed to redirect the interest of the user'
|
|
||||||
string public constant CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
|
string public constant CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
|
||||||
string public constant TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
|
string public constant TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
|
||||||
string public constant INTEREST_ALREADY_REDIRECTED = '32'; // 'Interest is already redirected to the user'
|
|
||||||
string public constant NO_VALID_BALANCE_FOR_REDIRECTION = '33'; // 'Interest stream can only be redirected if there is a valid balance'
|
|
||||||
string public constant INVALID_ATOKEN_BALANCE = '52'; // balance on burning is invalid
|
string public constant INVALID_ATOKEN_BALANCE = '52'; // balance on burning is invalid
|
||||||
|
|
||||||
// require error messages - ReserveLogic
|
// require error messages - ReserveLogic
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ library GenericLogic {
|
||||||
) external view returns (bool) {
|
) external view returns (bool) {
|
||||||
if (
|
if (
|
||||||
!userConfig.isBorrowingAny() ||
|
!userConfig.isBorrowingAny() ||
|
||||||
!userConfig.isUsingAsCollateral(reservesData[asset].index)
|
!userConfig.isUsingAsCollateral(reservesData[asset].id)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,23 +51,27 @@ library ReserveLogic {
|
||||||
struct ReserveData {
|
struct ReserveData {
|
||||||
//stores the reserve configuration
|
//stores the reserve configuration
|
||||||
ReserveConfiguration.Map configuration;
|
ReserveConfiguration.Map configuration;
|
||||||
address aTokenAddress;
|
|
||||||
address stableDebtTokenAddress;
|
|
||||||
address variableDebtTokenAddress;
|
|
||||||
address interestRateStrategyAddress;
|
|
||||||
//the liquidity index. Expressed in ray
|
//the liquidity index. Expressed in ray
|
||||||
uint128 liquidityIndex;
|
uint128 liquidityIndex;
|
||||||
|
//variable borrow index. Expressed in ray
|
||||||
|
uint128 variableBorrowIndex;
|
||||||
//the current supply rate. Expressed in ray
|
//the current supply rate. Expressed in ray
|
||||||
uint128 currentLiquidityRate;
|
uint128 currentLiquidityRate;
|
||||||
//the current variable borrow rate. Expressed in ray
|
//the current variable borrow rate. Expressed in ray
|
||||||
uint128 currentVariableBorrowRate;
|
uint128 currentVariableBorrowRate;
|
||||||
//the current stable borrow rate. Expressed in ray
|
//the current stable borrow rate. Expressed in ray
|
||||||
uint128 currentStableBorrowRate;
|
uint128 currentStableBorrowRate;
|
||||||
//variable borrow index. Expressed in ray
|
|
||||||
uint128 lastVariableBorrowIndex;
|
|
||||||
uint40 lastUpdateTimestamp;
|
uint40 lastUpdateTimestamp;
|
||||||
//the index of the reserve in the list of the active reserves
|
|
||||||
uint8 index;
|
//tokens addresses
|
||||||
|
address aTokenAddress;
|
||||||
|
address stableDebtTokenAddress;
|
||||||
|
address variableDebtTokenAddress;
|
||||||
|
|
||||||
|
address interestRateStrategyAddress;
|
||||||
|
|
||||||
|
//the id of the reserve. Represents the position in the list of the active reserves
|
||||||
|
uint8 id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -106,16 +110,38 @@ library ReserveLogic {
|
||||||
//solium-disable-next-line
|
//solium-disable-next-line
|
||||||
if (timestamp == uint40(block.timestamp)) {
|
if (timestamp == uint40(block.timestamp)) {
|
||||||
//if the index was updated in the same block, no need to perform any calculation
|
//if the index was updated in the same block, no need to perform any calculation
|
||||||
return reserve.lastVariableBorrowIndex;
|
return reserve.variableBorrowIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 cumulated = MathUtils
|
uint256 cumulated = MathUtils
|
||||||
.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp)
|
.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp)
|
||||||
.rayMul(reserve.lastVariableBorrowIndex);
|
.rayMul(reserve.variableBorrowIndex);
|
||||||
|
|
||||||
return cumulated;
|
return cumulated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns an address of the debt token used for particular interest rate mode on asset.
|
||||||
|
* @param reserve the reserve object
|
||||||
|
* @param interestRateMode - STABLE or VARIABLE from ReserveLogic.InterestRateMode enum
|
||||||
|
* @return an address of the corresponding debt token from reserve configuration
|
||||||
|
**/
|
||||||
|
function getDebtTokenAddress(ReserveLogic.ReserveData storage reserve, uint256 interestRateMode)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
require(
|
||||||
|
ReserveLogic.InterestRateMode.STABLE == ReserveLogic.InterestRateMode(interestRateMode) ||
|
||||||
|
ReserveLogic.InterestRateMode.VARIABLE == ReserveLogic.InterestRateMode(interestRateMode),
|
||||||
|
Errors.INVALID_INTEREST_RATE_MODE_SELECTED
|
||||||
|
);
|
||||||
|
return
|
||||||
|
ReserveLogic.InterestRateMode.STABLE == ReserveLogic.InterestRateMode(interestRateMode)
|
||||||
|
? reserve.stableDebtTokenAddress
|
||||||
|
: reserve.variableDebtTokenAddress;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for
|
* @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for
|
||||||
* a formal specification.
|
* a formal specification.
|
||||||
|
|
@ -143,9 +169,9 @@ library ReserveLogic {
|
||||||
reserve.currentVariableBorrowRate,
|
reserve.currentVariableBorrowRate,
|
||||||
lastUpdateTimestamp
|
lastUpdateTimestamp
|
||||||
);
|
);
|
||||||
index = cumulatedVariableBorrowInterest.rayMul(reserve.lastVariableBorrowIndex);
|
index = cumulatedVariableBorrowInterest.rayMul(reserve.variableBorrowIndex);
|
||||||
require(index < (1 << 128), Errors.VARIABLE_BORROW_INDEX_OVERFLOW);
|
require(index < (1 << 128), Errors.VARIABLE_BORROW_INDEX_OVERFLOW);
|
||||||
reserve.lastVariableBorrowIndex = uint128(index);
|
reserve.variableBorrowIndex = uint128(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,8 +220,8 @@ library ReserveLogic {
|
||||||
reserve.liquidityIndex = uint128(WadRayMath.ray());
|
reserve.liquidityIndex = uint128(WadRayMath.ray());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reserve.lastVariableBorrowIndex == 0) {
|
if (reserve.variableBorrowIndex == 0) {
|
||||||
reserve.lastVariableBorrowIndex = uint128(WadRayMath.ray());
|
reserve.variableBorrowIndex = uint128(WadRayMath.ray());
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve.aTokenAddress = aTokenAddress;
|
reserve.aTokenAddress = aTokenAddress;
|
||||||
|
|
@ -260,7 +286,7 @@ library ReserveLogic {
|
||||||
vars.currentAvgStableRate,
|
vars.currentAvgStableRate,
|
||||||
vars.newVariableRate,
|
vars.newVariableRate,
|
||||||
reserve.liquidityIndex,
|
reserve.liquidityIndex,
|
||||||
reserve.lastVariableBorrowIndex
|
reserve.variableBorrowIndex
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ library ValidationLogic {
|
||||||
/**
|
/**
|
||||||
* @dev validates a borrow.
|
* @dev validates a borrow.
|
||||||
* @param reserve the reserve state from which the user is borrowing
|
* @param reserve the reserve state from which the user is borrowing
|
||||||
* @param reserveAddress the address of the reserve
|
* @param userAddress the address of the user
|
||||||
* @param amount the amount to be borrowed
|
* @param amount the amount to be borrowed
|
||||||
* @param amountInETH the amount to be borrowed, in ETH
|
* @param amountInETH the amount to be borrowed, in ETH
|
||||||
* @param interestRateMode the interest rate mode at which the user is borrowing
|
* @param interestRateMode the interest rate mode at which the user is borrowing
|
||||||
|
|
@ -114,7 +114,7 @@ library ValidationLogic {
|
||||||
|
|
||||||
function validateBorrow(
|
function validateBorrow(
|
||||||
ReserveLogic.ReserveData storage reserve,
|
ReserveLogic.ReserveData storage reserve,
|
||||||
address reserveAddress,
|
address userAddress,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 amountInETH,
|
uint256 amountInETH,
|
||||||
uint256 interestRateMode,
|
uint256 interestRateMode,
|
||||||
|
|
@ -152,7 +152,7 @@ library ValidationLogic {
|
||||||
vars.currentLiquidationThreshold,
|
vars.currentLiquidationThreshold,
|
||||||
vars.healthFactor
|
vars.healthFactor
|
||||||
) = GenericLogic.calculateUserAccountData(
|
) = GenericLogic.calculateUserAccountData(
|
||||||
msg.sender,
|
userAddress,
|
||||||
reservesData,
|
reservesData,
|
||||||
userConfig,
|
userConfig,
|
||||||
reserves,
|
reserves,
|
||||||
|
|
@ -191,9 +191,9 @@ library ValidationLogic {
|
||||||
require(vars.stableRateBorrowingEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
|
require(vars.stableRateBorrowingEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
!userConfig.isUsingAsCollateral(reserve.index) ||
|
!userConfig.isUsingAsCollateral(reserve.id) ||
|
||||||
reserve.configuration.getLtv() == 0 ||
|
reserve.configuration.getLtv() == 0 ||
|
||||||
amount > IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
|
amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress),
|
||||||
Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
|
Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -275,7 +275,7 @@ library ValidationLogic {
|
||||||
require(stableRateEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
|
require(stableRateEnabled, Errors.STABLE_BORROWING_NOT_ENABLED);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
!userConfig.isUsingAsCollateral(reserve.index) ||
|
!userConfig.isUsingAsCollateral(reserve.id) ||
|
||||||
reserve.configuration.getLtv() == 0 ||
|
reserve.configuration.getLtv() == 0 ||
|
||||||
stableBorrowBalance.add(variableBorrowBalance) >
|
stableBorrowBalance.add(variableBorrowBalance) >
|
||||||
IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
|
IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
|
||||||
|
|
@ -364,7 +364,7 @@ library ValidationLogic {
|
||||||
|
|
||||||
bool isCollateralEnabled =
|
bool isCollateralEnabled =
|
||||||
collateralReserve.configuration.getLiquidationThreshold() > 0 &&
|
collateralReserve.configuration.getLiquidationThreshold() > 0 &&
|
||||||
userConfig.isUsingAsCollateral(collateralReserve.index);
|
userConfig.isUsingAsCollateral(collateralReserve.id);
|
||||||
|
|
||||||
//if collateral isn't enabled as collateral by user, it cannot be liquidated
|
//if collateral isn't enabled as collateral by user, it cannot be liquidated
|
||||||
if (!isCollateralEnabled) {
|
if (!isCollateralEnabled) {
|
||||||
|
|
@ -422,7 +422,7 @@ library ValidationLogic {
|
||||||
if (msg.sender != user) {
|
if (msg.sender != user) {
|
||||||
bool isCollateralEnabled =
|
bool isCollateralEnabled =
|
||||||
collateralReserve.configuration.getLiquidationThreshold() > 0 &&
|
collateralReserve.configuration.getLiquidationThreshold() > 0 &&
|
||||||
userConfig.isUsingAsCollateral(collateralReserve.index);
|
userConfig.isUsingAsCollateral(collateralReserve.id);
|
||||||
|
|
||||||
//if collateral isn't enabled as collateral by user, it cannot be liquidated
|
//if collateral isn't enabled as collateral by user, it cannot be liquidated
|
||||||
if (!isCollateralEnabled) {
|
if (!isCollateralEnabled) {
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,18 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
||||||
using SafeERC20 for ERC20;
|
using SafeERC20 for ERC20;
|
||||||
|
|
||||||
uint256 public constant UINT_MAX_VALUE = uint256(-1);
|
uint256 public constant UINT_MAX_VALUE = uint256(-1);
|
||||||
|
|
||||||
address public immutable UNDERLYING_ASSET_ADDRESS;
|
address public immutable UNDERLYING_ASSET_ADDRESS;
|
||||||
LendingPool public immutable POOL;
|
LendingPool public immutable POOL;
|
||||||
|
|
||||||
mapping(address => uint256) private _scaledRedirectedBalances;
|
/// @dev owner => next valid nonce to submit with permit()
|
||||||
|
mapping (address => uint256) public _nonces;
|
||||||
|
|
||||||
uint256 public constant ATOKEN_REVISION = 0x1;
|
uint256 public constant ATOKEN_REVISION = 0x1;
|
||||||
|
|
||||||
|
bytes32 public DOMAIN_SEPARATOR;
|
||||||
|
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)");
|
||||||
|
|
||||||
modifier onlyLendingPool {
|
modifier onlyLendingPool {
|
||||||
require(msg.sender == address(POOL), Errors.CALLER_MUST_BE_LENDING_POOL);
|
require(msg.sender == address(POOL), Errors.CALLER_MUST_BE_LENDING_POOL);
|
||||||
|
|
@ -56,6 +60,21 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
||||||
string calldata tokenName,
|
string calldata tokenName,
|
||||||
string calldata tokenSymbol
|
string calldata tokenSymbol
|
||||||
) external virtual initializer {
|
) external virtual initializer {
|
||||||
|
uint256 chainId;
|
||||||
|
|
||||||
|
//solium-disable-next-line
|
||||||
|
assembly {
|
||||||
|
chainId := chainid()
|
||||||
|
}
|
||||||
|
|
||||||
|
DOMAIN_SEPARATOR = keccak256(abi.encode(
|
||||||
|
EIP712_DOMAIN,
|
||||||
|
keccak256(bytes(tokenName)),
|
||||||
|
keccak256(EIP712_REVISION),
|
||||||
|
chainId,
|
||||||
|
address(this)
|
||||||
|
));
|
||||||
|
|
||||||
_setName(tokenName);
|
_setName(tokenName);
|
||||||
_setSymbol(tokenSymbol);
|
_setSymbol(tokenSymbol);
|
||||||
_setDecimals(underlyingAssetDecimals);
|
_setDecimals(underlyingAssetDecimals);
|
||||||
|
|
@ -129,9 +148,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
||||||
* @return the total balance of the user
|
* @return the total balance of the user
|
||||||
**/
|
**/
|
||||||
function balanceOf(address user) public override(ERC20, IERC20) view returns (uint256) {
|
function balanceOf(address user) public override(ERC20, IERC20) view returns (uint256) {
|
||||||
|
|
||||||
return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
|
return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -189,6 +206,42 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
function _transfer(
|
function _transfer(
|
||||||
address from,
|
address from,
|
||||||
address to,
|
address to,
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ interface IAToken is IERC20 {
|
||||||
* @dev transfer the amount of the underlying asset to the user
|
* @dev transfer the amount of the underlying asset to the user
|
||||||
* @param user address of the user
|
* @param user address of the user
|
||||||
* @param amount the amount to transfer
|
* @param amount the amount to transfer
|
||||||
* @return the redirected balance index
|
* @return the amount transferred
|
||||||
**/
|
**/
|
||||||
|
|
||||||
function transferUnderlyingTo(address user, uint256 amount) external returns (uint256);
|
function transferUnderlyingTo(address user, uint256 amount) external returns (uint256);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x209bb253C2f894D3Cc53b9dC23d308Eb8593613A",
|
"address": "0x9Dc554694756dC303a087e04bA6918C333Bc26a7",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xa2eDbC6b9E7EBA4b66f6A0B8Af3065CaC5611A6E",
|
"address": "0xAfC307938C1c0035942c141c31524504c89Aaa8B",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x6424b49739C3fC1d390Cea7A6bafa5B32A7B47b8",
|
"address": "0x73DE1e0ab6A5C221258703bc546E0CAAcCc6EC87",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
"address": "0x6642B57e4265BAD868C17Fc1d1F4F88DBBA04Aa8"
|
"address": "0x6642B57e4265BAD868C17Fc1d1F4F88DBBA04Aa8"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x8Be07B1e05bCB4091344ff2356D40EBf7e0c9b6E"
|
"address": "0x65e0Cd5B8904A02f2e00BC6f58bf881998D54BDe"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"LendingPoolDataProvider": {
|
"LendingPoolDataProvider": {
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
"address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e"
|
"address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xB44f879C781DfFF5E07aF7d338449E767Aa1c1d2"
|
"address": "0x5d12dDe3286D94E0d85F9D3B01B7099cfA0aBCf1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"PriceOracle": {
|
"PriceOracle": {
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x34c94f172B5eAcb53230AE62e41e1828B1a4B0F8",
|
"address": "0xbeA90474c2F3C7c43bC7c36CaAf5272c927Af5a1",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x01C5292e57aB25E38152ceE4A45C2038f233D531",
|
"address": "0x19E42cA990cF697D3dda0e59131215C43bB6989F",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x3D6bB48D5988B0D8B1d920cef50f59ED7d527F8c",
|
"address": "0xE30c3983E51bC9d6baE3E9437710a1459e21e81F",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -105,7 +105,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xC414d0323C57aF313F570A09c1D6e691F3C1c195",
|
"address": "0xDf69898e844197a24C658CcF9fD53dF15948dc8b",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xbEed026d89A715F28b32135A6C3e3c060234f40d",
|
"address": "0xBe6d8642382C241c9B4B50c89574DbF3f4181E7D",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -169,7 +169,7 @@
|
||||||
"address": "0x2B681757d757fbB80cc51c6094cEF5eE75bF55aA"
|
"address": "0x2B681757d757fbB80cc51c6094cEF5eE75bF55aA"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x045Da1BcEB75D2E0064a66Da1Ad606350CD9730A"
|
"address": "0xAd49512dFBaD6fc13D67d3935283c0606812E962"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"WalletBalanceProvider": {
|
"WalletBalanceProvider": {
|
||||||
|
|
@ -178,7 +178,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xd54dbF2a2D88aFeCA7E288D43e16150b57C6bcd9",
|
"address": "0xA29C2A7e59aa49C71aF084695337E3AA5e820758",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -188,7 +188,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xa17a59441D6c39D21F2Ff03e7b21f8d2BCAA6023",
|
"address": "0xbe66dC9DFEe580ED968403e35dF7b5159f873df8",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -198,7 +198,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x36B6f7e34d651DC7fd7EeEc53bf26594209915A8",
|
"address": "0x93AfC6Df4bB8F62F2493B19e577f8382c0BA9EBC",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -208,7 +208,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x099E8e561f4cfCe0bbDaD063d973eBcf1A1d92B5",
|
"address": "0x75Ded61646B5945BdDd0CD9a9Db7c8288DA6F810",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -218,7 +218,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x8473D688815861639F6e4442F4433047c2F5571b",
|
"address": "0xdE7c40e675bF1aA45c18cCbaEb9662B16b0Ddf7E",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -228,7 +228,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xBcf29d6fd8EB2d95b5Ad0Ffdd7ee5272cc49b26c",
|
"address": "0xDFbeeed692AA81E7f86E72F7ACbEA2A1C4d63544",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -238,7 +238,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x6e77C7e0Fd33A53351335E768593ba56A6C43594",
|
"address": "0x5191aA68c7dB195181Dd2441dBE23A48EA24b040",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -248,7 +248,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x67a87Be0A08F955EfC629b1cdaaE1eaC48043E6a",
|
"address": "0x8F9422aa37215c8b3D1Ea1674138107F84D68F26",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -258,7 +258,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xEDf104A35B3293F4BdB987be9D57EFe3b69C19c7",
|
"address": "0xa89E20284Bd638F31b0011D0fC754Fc9d2fa73e3",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -268,7 +268,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xD8212F51E19A269B8fCc327BF91ede79e218EF17",
|
"address": "0xaA935993065F2dDB1d13623B1941C7AEE3A60F23",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -278,7 +278,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x1712cE132Cc5E2A5b63e6AF4Ee551070f7Bc4487",
|
"address": "0x35A2624888e207e4B3434E9a9E250bF6Ee68FeA3",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -288,7 +288,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x6DC0873546006Ce00eC8AA9e97706125D75E3ab6",
|
"address": "0x1f569c307949a908A4b8Ff7453a88Ca0b8D8df13",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -298,7 +298,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x133EA40EA9975d53D34417F9164a54A635110AE9",
|
"address": "0x4301cb254CCc126B9eb9cbBE030C6FDA2FA16D4a",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -308,7 +308,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xfe5E2ac37e1cf3f1dFA55De53780692846eD199A",
|
"address": "0x0766c9592a8686CAB0081b4f35449462c6e82F11",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -318,7 +318,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xcf33a1c13D1599399B3581c3f3cf39e943013A73",
|
"address": "0xaF6D34adD35E1A565be4539E4d1069c48A49C953",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -328,7 +328,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x6B4Fef015Ea5D2A23C5E5906b41f206c79E36316",
|
"address": "0x48bb3E35D2D6994374db457a6Bf61de2d9cC8E49",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -338,7 +338,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x556f80053f02Ee04a4f13820AE7a30f787A7A630",
|
"address": "0x1E59BA56B1F61c3Ee946D8c7e2994B4A9b0cA45C",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -348,7 +348,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x222C21A948139f016EBbd1979250194049b28473",
|
"address": "0x53813198c75959DDB604462831d8989C29152164",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -358,7 +358,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xb0c6bAc77c65588a5c47d18545D3d265b0030B7e",
|
"address": "0x0eD6115873ce6B807a03FE0df1f940387779b729",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -368,7 +368,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x9197B2985256CD8a0B41796ab5794D502012766c",
|
"address": "0xFFfDa24e7E3d5F89a24278f53d6f0F81B3bE0d6B",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -378,7 +378,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x89E72D113048277a670222d9bcACF4FA2c7b20A6",
|
"address": "0x5889354f21A1C8D8D2f82669d778f6Dab778B519",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -388,7 +388,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x6f4689b37FCC44f24e8dE9Cf2B61f81E71fB9dc0",
|
"address": "0x09F7bF33B3F8922268B34103af3a8AF83148C9B1",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -398,7 +398,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x77183A4B7c0375bA9A5090Ae68c32A5C567d77c6",
|
"address": "0x8f3966F7d53Fd5f12b701C8835e1e32541613869",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -408,7 +408,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x209bb253C2f894D3Cc53b9dC23d308Eb8593613A",
|
"address": "0x9Dc554694756dC303a087e04bA6918C333Bc26a7",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -417,7 +417,7 @@
|
||||||
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4"
|
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xc47cF1C70618CB94e6B1D218468D3E16AE35Fff4"
|
"address": "0x9305d862ee95a899b83906Cd9CB666aC269E5f66"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"StableDebtToken": {
|
"StableDebtToken": {
|
||||||
|
|
@ -426,7 +426,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x3888B5ac0089C12cDF21DD8B0234029f80645324",
|
"address": "0x02BB514187B830d6A2111197cd7D8cb60650B970",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -436,13 +436,13 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xB76Ea4df0263F99daf33765541b1933AD5bB4410",
|
"address": "0x6774Ce86Abf5EBB22E9F45b5f55daCbB4170aD7f",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AToken": {
|
"AToken": {
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x4d39D68f5a2A43E79e7B3A859014cc4233D0EEA1",
|
"address": "0x007C1a44e85bDa8F562F916685A9DC8BdC6542bF",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"buidlerevm": {
|
"buidlerevm": {
|
||||||
|
|
@ -456,7 +456,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x5efEaaE02a5E2BdA1aDAc7aad29D9B4bFFDD90E8",
|
"address": "0xFBdF1E93D0D88145e3CcA63bf8d513F83FB0903b",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -466,7 +466,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xd334C51Ad3167554876f19F9575394F1cfbc96AF",
|
"address": "0xEcb928A3c079a1696Aa5244779eEc3dE1717fACd",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -476,7 +476,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0x59A442D1DbAE607fD3cd97859dc14Ff400F7C2ed",
|
"address": "0xE45fF4A0A8D0E9734C73874c034E03594E15ba28",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -486,7 +486,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xE8F349DB32821021520BBe11b7927279BC3BEC6b",
|
"address": "0x5cCC6Abc4c9F7262B9485797a848Ec6CC28A11dF",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -495,7 +495,7 @@
|
||||||
"address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2"
|
"address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
"address": "0xcB70821E9dDE40dc23E973280991A8cdBFD4EC2c"
|
"address": "0x749258D38b0473d96FEcc14cC5e7DCE12d7Bd6f6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,12 +8,16 @@ import {
|
||||||
IReserveParams,
|
IReserveParams,
|
||||||
tEthereumAddress,
|
tEthereumAddress,
|
||||||
iBasicDistributionParams,
|
iBasicDistributionParams,
|
||||||
|
eEthereumNetwork,
|
||||||
} from './types';
|
} from './types';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import {getParamPerPool} from './contracts-helpers';
|
import {getParamPerPool, getParamPerNetwork} from './contracts-helpers';
|
||||||
|
|
||||||
export const TEST_SNAPSHOT_ID = '0x1';
|
export const TEST_SNAPSHOT_ID = '0x1';
|
||||||
|
|
||||||
|
export const BUIDLEREVM_CHAINID = 31337;
|
||||||
|
export const COVERAGE_CHAINID = 1337;
|
||||||
|
|
||||||
// ----------------
|
// ----------------
|
||||||
// MATH
|
// MATH
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
@ -531,3 +535,18 @@ export const getFeeDistributionParamsCommon = (
|
||||||
percentages,
|
percentages,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getATokenDomainSeparatorPerNetwork = (
|
||||||
|
network: eEthereumNetwork
|
||||||
|
): tEthereumAddress =>
|
||||||
|
getParamPerNetwork<tEthereumAddress>(
|
||||||
|
{
|
||||||
|
[eEthereumNetwork.coverage]: "0x95b73a72c6ecf4ccbbba5178800023260bad8e75cdccdb8e4827a2977a37c820",
|
||||||
|
[eEthereumNetwork.buidlerevm]:
|
||||||
|
"0x76cbbf8aa4b11a7c207dd79ccf8c394f59475301598c9a083f8258b4fafcfa86",
|
||||||
|
[eEthereumNetwork.kovan]: "",
|
||||||
|
[eEthereumNetwork.ropsten]: "",
|
||||||
|
[eEthereumNetwork.main]: "",
|
||||||
|
},
|
||||||
|
network
|
||||||
|
);
|
||||||
|
|
@ -32,6 +32,8 @@ import {Ierc20Detailed} from '../types/Ierc20Detailed';
|
||||||
import {StableDebtToken} from '../types/StableDebtToken';
|
import {StableDebtToken} from '../types/StableDebtToken';
|
||||||
import {VariableDebtToken} from '../types/VariableDebtToken';
|
import {VariableDebtToken} from '../types/VariableDebtToken';
|
||||||
import {MockSwapAdapter} from '../types/MockSwapAdapter';
|
import {MockSwapAdapter} from '../types/MockSwapAdapter';
|
||||||
|
import { signTypedData_v4, TypedData } from "eth-sig-util";
|
||||||
|
import { fromRpcSig, ECDSASignature } from "ethereumjs-util";
|
||||||
|
|
||||||
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
|
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
|
||||||
const currentNetwork = BRE.network.name;
|
const currentNetwork = BRE.network.name;
|
||||||
|
|
@ -431,10 +433,14 @@ const linkBytecode = (artifact: Artifact, libraries: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getParamPerNetwork = <T>(
|
export const getParamPerNetwork = <T>(
|
||||||
{kovan, ropsten, main}: iParamsPerNetwork<T>,
|
{kovan, ropsten, main, buidlerevm, coverage}: iParamsPerNetwork<T>,
|
||||||
network: eEthereumNetwork
|
network: eEthereumNetwork
|
||||||
) => {
|
) => {
|
||||||
switch (network) {
|
switch (network) {
|
||||||
|
case eEthereumNetwork.coverage:
|
||||||
|
return coverage;
|
||||||
|
case eEthereumNetwork.buidlerevm:
|
||||||
|
return buidlerevm;
|
||||||
case eEthereumNetwork.kovan:
|
case eEthereumNetwork.kovan:
|
||||||
return kovan;
|
return kovan;
|
||||||
case eEthereumNetwork.ropsten:
|
case eEthereumNetwork.ropsten:
|
||||||
|
|
@ -471,3 +477,59 @@ export const convertToCurrencyUnits = async (tokenAddress: string, amount: strin
|
||||||
const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit);
|
const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit);
|
||||||
return amountInCurrencyUnits.toFixed();
|
return amountInCurrencyUnits.toFixed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const buildPermitParams = (
|
||||||
|
chainId: number,
|
||||||
|
token: tEthereumAddress,
|
||||||
|
revision: string,
|
||||||
|
tokenName: string,
|
||||||
|
owner: tEthereumAddress,
|
||||||
|
spender: tEthereumAddress,
|
||||||
|
nonce: number,
|
||||||
|
deadline: string,
|
||||||
|
value: tStringTokenSmallUnits
|
||||||
|
) => ({
|
||||||
|
types: {
|
||||||
|
EIP712Domain: [
|
||||||
|
{ name: "name", type: "string" },
|
||||||
|
{ name: "version", type: "string" },
|
||||||
|
{ name: "chainId", type: "uint256" },
|
||||||
|
{ name: "verifyingContract", type: "address" },
|
||||||
|
],
|
||||||
|
Permit: [
|
||||||
|
{ name: "owner", type: "address" },
|
||||||
|
{ name: "spender", type: "address" },
|
||||||
|
{ name: "value", type: "uint256" },
|
||||||
|
{ name: "nonce", type: "uint256" },
|
||||||
|
{ name: "deadline", type: "uint256" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
primaryType: "Permit" as const,
|
||||||
|
domain: {
|
||||||
|
name: tokenName,
|
||||||
|
version: revision,
|
||||||
|
chainId: chainId,
|
||||||
|
verifyingContract: token,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
owner,
|
||||||
|
spender,
|
||||||
|
value,
|
||||||
|
nonce,
|
||||||
|
deadline,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const getSignatureFromTypedData = (
|
||||||
|
privateKey: string,
|
||||||
|
typedData: any // TODO: should be TypedData, from eth-sig-utils, but TS doesn't accept it
|
||||||
|
): ECDSASignature => {
|
||||||
|
const signature = signTypedData_v4(
|
||||||
|
Buffer.from(privateKey.substring(2, 66), "hex"),
|
||||||
|
{
|
||||||
|
data: typedData,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return fromRpcSig(signature);
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,7 @@ export enum eEthereumNetwork {
|
||||||
kovan = 'kovan',
|
kovan = 'kovan',
|
||||||
ropsten = 'ropsten',
|
ropsten = 'ropsten',
|
||||||
main = 'main',
|
main = 'main',
|
||||||
|
coverage = 'coverage'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AavePools {
|
export enum AavePools {
|
||||||
|
|
@ -78,11 +79,8 @@ export enum ProtocolErrors {
|
||||||
|
|
||||||
// require error messages - aToken
|
// require error messages - aToken
|
||||||
CALLER_MUST_BE_LENDING_POOL = '28', // 'The caller of this function must be a lending pool'
|
CALLER_MUST_BE_LENDING_POOL = '28', // 'The caller of this function must be a lending pool'
|
||||||
INTEREST_REDIRECTION_NOT_ALLOWED = '29', // 'Caller is not allowed to redirect the interest of the user'
|
|
||||||
CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30', // 'User cannot give allowance to himself'
|
CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30', // 'User cannot give allowance to himself'
|
||||||
TRANSFER_AMOUNT_NOT_GT_0 = '31', // 'Transferred amount needs to be greater than zero'
|
TRANSFER_AMOUNT_NOT_GT_0 = '31', // 'Transferred amount needs to be greater than zero'
|
||||||
INTEREST_ALREADY_REDIRECTED = '32', // 'Interest is already redirected to the user'
|
|
||||||
NO_VALID_BALANCE_FOR_REDIRECTION = '33', // 'Interest stream can only be redirected if there is a valid balance'
|
|
||||||
|
|
||||||
// require error messages - ReserveLogic
|
// require error messages - ReserveLogic
|
||||||
RESERVE_ALREADY_INITIALIZED = '34', // 'Reserve has already been initialized'
|
RESERVE_ALREADY_INITIALIZED = '34', // 'Reserve has already been initialized'
|
||||||
|
|
@ -107,9 +105,6 @@ export enum ProtocolErrors {
|
||||||
INVALID_FROM_BALANCE_AFTER_TRANSFER = 'Invalid from balance after transfer',
|
INVALID_FROM_BALANCE_AFTER_TRANSFER = 'Invalid from balance after transfer',
|
||||||
INVALID_TO_BALANCE_AFTER_TRANSFER = 'Invalid from balance after transfer',
|
INVALID_TO_BALANCE_AFTER_TRANSFER = 'Invalid from balance after transfer',
|
||||||
INVALID_OWNER_REVERT_MSG = 'Ownable: caller is not the owner',
|
INVALID_OWNER_REVERT_MSG = 'Ownable: caller is not the owner',
|
||||||
INVALID_REDIRECTED_BALANCE_BEFORE_TRANSFER = 'Invalid redirected balance before transfer',
|
|
||||||
INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER = 'Invalid redirected balance after transfer',
|
|
||||||
INVALID_REDIRECTION_ADDRESS = 'Invalid redirection address',
|
|
||||||
INVALID_HF = 'Invalid health factor',
|
INVALID_HF = 'Invalid health factor',
|
||||||
TRANSFER_AMOUNT_EXCEEDS_BALANCE = 'ERC20: transfer amount exceeds balance',
|
TRANSFER_AMOUNT_EXCEEDS_BALANCE = 'ERC20: transfer amount exceeds balance',
|
||||||
SAFEERC20_LOWLEVEL_CALL = 'SafeERC20: low-level call failed',
|
SAFEERC20_LOWLEVEL_CALL = 'SafeERC20: low-level call failed',
|
||||||
|
|
@ -251,6 +246,8 @@ export interface IMarketRates {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iParamsPerNetwork<T> {
|
export interface iParamsPerNetwork<T> {
|
||||||
|
[eEthereumNetwork.coverage]: T;
|
||||||
|
[eEthereumNetwork.buidlerevm]: T;
|
||||||
[eEthereumNetwork.kovan]: T;
|
[eEthereumNetwork.kovan]: T;
|
||||||
[eEthereumNetwork.ropsten]: T;
|
[eEthereumNetwork.ropsten]: T;
|
||||||
[eEthereumNetwork.main]: T;
|
[eEthereumNetwork.main]: T;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"test-repay-with-collateral": "buidler test test/__setup.spec.ts test/repay-with-collateral.spec.ts",
|
"test-repay-with-collateral": "buidler test test/__setup.spec.ts test/repay-with-collateral.spec.ts",
|
||||||
"test-liquidate-with-collateral": "buidler test test/__setup.spec.ts test/flash-liquidation-with-collateral.spec.ts",
|
"test-liquidate-with-collateral": "buidler test test/__setup.spec.ts test/flash-liquidation-with-collateral.spec.ts",
|
||||||
"test-flash": "buidler test test/__setup.spec.ts test/flashloan.spec.ts",
|
"test-flash": "buidler test test/__setup.spec.ts test/flashloan.spec.ts",
|
||||||
|
"test-permit": "buidler test test/__setup.spec.ts test/atoken-permit.spec.ts",
|
||||||
"dev:coverage": "buidler coverage --network coverage",
|
"dev:coverage": "buidler coverage --network coverage",
|
||||||
"dev:deployment": "buidler dev-deployment",
|
"dev:deployment": "buidler dev-deployment",
|
||||||
"dev:deployExample": "buidler deploy-Example",
|
"dev:deployExample": "buidler deploy-Example",
|
||||||
|
|
@ -58,7 +59,9 @@
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"tslint-plugin-prettier": "^2.3.0",
|
"tslint-plugin-prettier": "^2.3.0",
|
||||||
"typechain": "2.0.0",
|
"typechain": "2.0.0",
|
||||||
"typescript": "3.9.3"
|
"typescript": "3.9.3",
|
||||||
|
"eth-sig-util": "2.5.3",
|
||||||
|
"ethereumjs-util": "7.0.2"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
|
|
||||||
312
test/atoken-permit.spec.ts
Normal file
312
test/atoken-permit.spec.ts
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
import {
|
||||||
|
MAX_UINT_AMOUNT,
|
||||||
|
ZERO_ADDRESS,
|
||||||
|
getATokenDomainSeparatorPerNetwork,
|
||||||
|
BUIDLEREVM_CHAINID,
|
||||||
|
} from '../helpers/constants';
|
||||||
|
import {buildPermitParams, getSignatureFromTypedData} from '../helpers/contracts-helpers';
|
||||||
|
import {expect} from 'chai';
|
||||||
|
import {ethers} from 'ethers';
|
||||||
|
import {eEthereumNetwork} from '../helpers/types';
|
||||||
|
import {makeSuite, TestEnv} from './helpers/make-suite';
|
||||||
|
import {BRE} from '../helpers/misc-utils';
|
||||||
|
import {waitForTx} from './__setup.spec';
|
||||||
|
|
||||||
|
const {parseEther} = ethers.utils;
|
||||||
|
|
||||||
|
makeSuite('AToken: Permit', (testEnv: TestEnv) => {
|
||||||
|
it('Checks the domain separator', async () => {
|
||||||
|
const DOMAIN_SEPARATOR_ENCODED = getATokenDomainSeparatorPerNetwork(
|
||||||
|
eEthereumNetwork.buidlerevm
|
||||||
|
);
|
||||||
|
|
||||||
|
const {aDai} = testEnv;
|
||||||
|
|
||||||
|
const separator = await aDai.DOMAIN_SEPARATOR();
|
||||||
|
|
||||||
|
expect(separator).to.be.equal(DOMAIN_SEPARATOR_ENCODED, 'Invalid domain separator');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Get aDAI for tests', async () => {
|
||||||
|
const {dai, deployer, pool} = testEnv;
|
||||||
|
|
||||||
|
await dai.mint(parseEther('20000'));
|
||||||
|
await dai.approve(pool.address, parseEther('20000'));
|
||||||
|
await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Reverts submitting a permit with 0 expiration', async () => {
|
||||||
|
const {aDai, deployer, users} = testEnv;
|
||||||
|
const owner = deployer;
|
||||||
|
const spender = users[1];
|
||||||
|
|
||||||
|
const tokenName = await aDai.name();
|
||||||
|
|
||||||
|
const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
||||||
|
const expiration = 0;
|
||||||
|
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
||||||
|
const permitAmount = ethers.utils.parseEther('2').toString();
|
||||||
|
const msgParams = buildPermitParams(
|
||||||
|
chainId,
|
||||||
|
aDai.address,
|
||||||
|
'1',
|
||||||
|
tokenName,
|
||||||
|
owner.address,
|
||||||
|
spender.address,
|
||||||
|
nonce,
|
||||||
|
permitAmount,
|
||||||
|
expiration.toFixed()
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
||||||
|
if (!ownerPrivateKey) {
|
||||||
|
throw new Error('INVALID_OWNER_PK');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
||||||
|
'0',
|
||||||
|
'INVALID_ALLOWANCE_BEFORE_PERMIT'
|
||||||
|
);
|
||||||
|
|
||||||
|
const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
aDai
|
||||||
|
.connect(spender.signer)
|
||||||
|
.permit(owner.address, spender.address, permitAmount, expiration, v, r, s)
|
||||||
|
).to.be.revertedWith('INVALID_EXPIRATION');
|
||||||
|
|
||||||
|
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
||||||
|
'0',
|
||||||
|
'INVALID_ALLOWANCE_AFTER_PERMIT'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Submits a permit with maximum expiration length', async () => {
|
||||||
|
const {aDai, deployer, users} = testEnv;
|
||||||
|
const owner = deployer;
|
||||||
|
const spender = users[1];
|
||||||
|
|
||||||
|
const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
||||||
|
const deadline = MAX_UINT_AMOUNT;
|
||||||
|
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
||||||
|
const permitAmount = parseEther('2').toString();
|
||||||
|
const msgParams = buildPermitParams(
|
||||||
|
chainId,
|
||||||
|
aDai.address,
|
||||||
|
'1',
|
||||||
|
await aDai.name(),
|
||||||
|
owner.address,
|
||||||
|
spender.address,
|
||||||
|
nonce,
|
||||||
|
deadline,
|
||||||
|
permitAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
||||||
|
if (!ownerPrivateKey) {
|
||||||
|
throw new Error('INVALID_OWNER_PK');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
||||||
|
'0',
|
||||||
|
'INVALID_ALLOWANCE_BEFORE_PERMIT'
|
||||||
|
);
|
||||||
|
|
||||||
|
const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
||||||
|
|
||||||
|
await waitForTx(
|
||||||
|
await aDai
|
||||||
|
.connect(spender.signer)
|
||||||
|
.permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Cancels the previous permit', async () => {
|
||||||
|
const {aDai, deployer, users} = testEnv;
|
||||||
|
const owner = deployer;
|
||||||
|
const spender = users[1];
|
||||||
|
|
||||||
|
const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
||||||
|
const deadline = MAX_UINT_AMOUNT;
|
||||||
|
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
||||||
|
const permitAmount = '0';
|
||||||
|
const msgParams = buildPermitParams(
|
||||||
|
chainId,
|
||||||
|
aDai.address,
|
||||||
|
'1',
|
||||||
|
await aDai.name(),
|
||||||
|
owner.address,
|
||||||
|
spender.address,
|
||||||
|
nonce,
|
||||||
|
deadline,
|
||||||
|
permitAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
||||||
|
if (!ownerPrivateKey) {
|
||||||
|
throw new Error('INVALID_OWNER_PK');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
||||||
|
|
||||||
|
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
||||||
|
ethers.utils.parseEther('2'),
|
||||||
|
'INVALID_ALLOWANCE_BEFORE_PERMIT'
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitForTx(
|
||||||
|
await aDai
|
||||||
|
.connect(spender.signer)
|
||||||
|
.permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
|
||||||
|
);
|
||||||
|
expect((await aDai.allowance(owner.address, spender.address)).toString()).to.be.equal(
|
||||||
|
permitAmount,
|
||||||
|
'INVALID_ALLOWANCE_AFTER_PERMIT'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect((await aDai._nonces(owner.address)).toNumber()).to.be.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Tries to submit a permit with invalid nonce', async () => {
|
||||||
|
const {aDai, deployer, users} = testEnv;
|
||||||
|
const owner = deployer;
|
||||||
|
const spender = users[1];
|
||||||
|
|
||||||
|
const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
||||||
|
const deadline = MAX_UINT_AMOUNT;
|
||||||
|
const nonce = 1000;
|
||||||
|
const permitAmount = '0';
|
||||||
|
const msgParams = buildPermitParams(
|
||||||
|
chainId,
|
||||||
|
aDai.address,
|
||||||
|
'1',
|
||||||
|
await aDai.name(),
|
||||||
|
owner.address,
|
||||||
|
spender.address,
|
||||||
|
nonce,
|
||||||
|
deadline,
|
||||||
|
permitAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
||||||
|
if (!ownerPrivateKey) {
|
||||||
|
throw new Error('INVALID_OWNER_PK');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
aDai
|
||||||
|
.connect(spender.signer)
|
||||||
|
.permit(owner.address, spender.address, permitAmount, deadline, v, r, s)
|
||||||
|
).to.be.revertedWith('INVALID_SIGNATURE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Tries to submit a permit with invalid expiration (previous to the current block)', async () => {
|
||||||
|
const {aDai, deployer, users} = testEnv;
|
||||||
|
const owner = deployer;
|
||||||
|
const spender = users[1];
|
||||||
|
|
||||||
|
const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
||||||
|
const expiration = '1';
|
||||||
|
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
||||||
|
const permitAmount = '0';
|
||||||
|
const msgParams = buildPermitParams(
|
||||||
|
chainId,
|
||||||
|
aDai.address,
|
||||||
|
'1',
|
||||||
|
await aDai.name(),
|
||||||
|
owner.address,
|
||||||
|
spender.address,
|
||||||
|
nonce,
|
||||||
|
expiration,
|
||||||
|
permitAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
||||||
|
if (!ownerPrivateKey) {
|
||||||
|
throw new Error('INVALID_OWNER_PK');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
aDai
|
||||||
|
.connect(spender.signer)
|
||||||
|
.permit(owner.address, spender.address, expiration, permitAmount, v, r, s)
|
||||||
|
).to.be.revertedWith('INVALID_EXPIRATION');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Tries to submit a permit with invalid signature', async () => {
|
||||||
|
const {aDai, deployer, users} = testEnv;
|
||||||
|
const owner = deployer;
|
||||||
|
const spender = users[1];
|
||||||
|
|
||||||
|
const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
||||||
|
const deadline = MAX_UINT_AMOUNT;
|
||||||
|
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
||||||
|
const permitAmount = '0';
|
||||||
|
const msgParams = buildPermitParams(
|
||||||
|
chainId,
|
||||||
|
aDai.address,
|
||||||
|
'1',
|
||||||
|
await aDai.name(),
|
||||||
|
owner.address,
|
||||||
|
spender.address,
|
||||||
|
nonce,
|
||||||
|
deadline,
|
||||||
|
permitAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
||||||
|
if (!ownerPrivateKey) {
|
||||||
|
throw new Error('INVALID_OWNER_PK');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
aDai
|
||||||
|
.connect(spender.signer)
|
||||||
|
.permit(owner.address, ZERO_ADDRESS, permitAmount, deadline, v, r, s)
|
||||||
|
).to.be.revertedWith('INVALID_SIGNATURE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Tries to submit a permit with invalid owner', async () => {
|
||||||
|
const {aDai, deployer, users} = testEnv;
|
||||||
|
const owner = deployer;
|
||||||
|
const spender = users[1];
|
||||||
|
|
||||||
|
const chainId = BRE.network.config.chainId || BUIDLEREVM_CHAINID;
|
||||||
|
const expiration = MAX_UINT_AMOUNT;
|
||||||
|
const nonce = (await aDai._nonces(owner.address)).toNumber();
|
||||||
|
const permitAmount = '0';
|
||||||
|
const msgParams = buildPermitParams(
|
||||||
|
chainId,
|
||||||
|
aDai.address,
|
||||||
|
'1',
|
||||||
|
await aDai.name(),
|
||||||
|
owner.address,
|
||||||
|
spender.address,
|
||||||
|
nonce,
|
||||||
|
expiration,
|
||||||
|
permitAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
const ownerPrivateKey = require('../test-wallets.js').accounts[0].secretKey;
|
||||||
|
if (!ownerPrivateKey) {
|
||||||
|
throw new Error('INVALID_OWNER_PK');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {v, r, s} = getSignatureFromTypedData(ownerPrivateKey, msgParams);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
aDai
|
||||||
|
.connect(spender.signer)
|
||||||
|
.permit(ZERO_ADDRESS, spender.address, expiration, permitAmount, v, r, s)
|
||||||
|
).to.be.revertedWith('INVALID_OWNER');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -14,11 +14,7 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
||||||
const {
|
const {
|
||||||
INVALID_FROM_BALANCE_AFTER_TRANSFER,
|
INVALID_FROM_BALANCE_AFTER_TRANSFER,
|
||||||
INVALID_TO_BALANCE_AFTER_TRANSFER,
|
INVALID_TO_BALANCE_AFTER_TRANSFER,
|
||||||
INVALID_REDIRECTED_BALANCE_BEFORE_TRANSFER,
|
|
||||||
INVALID_REDIRECTED_BALANCE_AFTER_TRANSFER,
|
|
||||||
INVALID_REDIRECTION_ADDRESS,
|
|
||||||
// ZERO_COLLATERAL,
|
// ZERO_COLLATERAL,
|
||||||
TRANSFER_AMOUNT_NOT_GT_0,
|
|
||||||
COLLATERAL_BALANCE_IS_0,
|
COLLATERAL_BALANCE_IS_0,
|
||||||
TRANSFER_NOT_ALLOWED,
|
TRANSFER_NOT_ALLOWED,
|
||||||
} = ProtocolErrors;
|
} = ProtocolErrors;
|
||||||
|
|
@ -64,7 +60,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
||||||
await expect(
|
await expect(
|
||||||
pool
|
pool
|
||||||
.connect(users[1].signer)
|
.connect(users[1].signer)
|
||||||
.borrow(weth.address, ethers.utils.parseEther('0.1'), RateMode.Stable, AAVE_REFERRAL),
|
.borrow(
|
||||||
|
weth.address,
|
||||||
|
ethers.utils.parseEther('0.1'),
|
||||||
|
RateMode.Stable,
|
||||||
|
AAVE_REFERRAL,
|
||||||
|
users[1].address
|
||||||
|
),
|
||||||
COLLATERAL_BALANCE_IS_0
|
COLLATERAL_BALANCE_IS_0
|
||||||
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
|
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
|
||||||
});
|
});
|
||||||
|
|
@ -77,7 +79,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(users[1].signer)
|
.connect(users[1].signer)
|
||||||
.borrow(weth.address, ethers.utils.parseEther('0.1'), RateMode.Stable, AAVE_REFERRAL);
|
.borrow(
|
||||||
|
weth.address,
|
||||||
|
ethers.utils.parseEther('0.1'),
|
||||||
|
RateMode.Stable,
|
||||||
|
AAVE_REFERRAL,
|
||||||
|
users[1].address
|
||||||
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
|
aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,9 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
|
||||||
|
|
||||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 1, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 1, 0, user.address);
|
||||||
|
|
||||||
const {userData: wethUserDataBefore} = await getContractsData(
|
const {userData: wethUserDataBefore} = await getContractsData(
|
||||||
weth.address,
|
weth.address,
|
||||||
|
|
@ -203,7 +203,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
|
||||||
.toFixed(0)
|
.toFixed(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountUSDCToBorrow, 2, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountUSDCToBorrow, 2, 0, user.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('User 5 liquidates half the USDC loan of User 3 by swapping his WETH collateral', async () => {
|
it('User 5 liquidates half the USDC loan of User 3 by swapping his WETH collateral', async () => {
|
||||||
|
|
@ -464,7 +464,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
|
||||||
.toFixed(0)
|
.toFixed(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(dai.address, amountDAIToBorrow, 2, 0);
|
await pool.connect(user.signer).borrow(dai.address, amountDAIToBorrow, 2, 0, user.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
||||||
|
|
@ -736,7 +736,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
|
||||||
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
||||||
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
|
||||||
|
|
||||||
const amountToRepay = amountToBorrowVariable;
|
const amountToRepay = amountToBorrowVariable;
|
||||||
|
|
||||||
|
|
@ -844,7 +844,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
|
||||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Liquidator tries to liquidates User 5 USDC loan by swapping his WETH collateral, should revert due WETH collateral disabled', async () => {
|
it('Liquidator tries to liquidates User 5 USDC loan by swapping his WETH collateral, should revert due WETH collateral disabled', async () => {
|
||||||
|
|
|
||||||
|
|
@ -283,11 +283,41 @@ export const withdraw = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const delegateBorrowAllowance = async (
|
||||||
|
reserveSymbol: string,
|
||||||
|
amount: string,
|
||||||
|
interestRateMode: string,
|
||||||
|
user: SignerWithAddress,
|
||||||
|
receiver: tEthereumAddress,
|
||||||
|
expectedResult: string,
|
||||||
|
testEnv: TestEnv,
|
||||||
|
revertMessage?: string
|
||||||
|
) => {
|
||||||
|
const {pool} = testEnv;
|
||||||
|
|
||||||
|
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
||||||
|
const amountToDelegate = await convertToCurrencyDecimals(reserve, amount);
|
||||||
|
|
||||||
|
const delegateAllowancePromise = pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.delegateBorrowAllowance(reserve, receiver, interestRateMode, amountToDelegate.toString());
|
||||||
|
if (expectedResult === 'revert') {
|
||||||
|
await expect(delegateAllowancePromise, revertMessage).to.be.reverted;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
await delegateAllowancePromise;
|
||||||
|
expect(
|
||||||
|
(await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateMode)).toString()
|
||||||
|
).to.be.equal(amountToDelegate.toString(), 'borrowAllowance are set incorrectly');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const borrow = async (
|
export const borrow = async (
|
||||||
reserveSymbol: string,
|
reserveSymbol: string,
|
||||||
amount: string,
|
amount: string,
|
||||||
interestRateMode: string,
|
interestRateMode: string,
|
||||||
user: SignerWithAddress,
|
user: SignerWithAddress,
|
||||||
|
onBehalfOf: tEthereumAddress,
|
||||||
timeTravel: string,
|
timeTravel: string,
|
||||||
expectedResult: string,
|
expectedResult: string,
|
||||||
testEnv: TestEnv,
|
testEnv: TestEnv,
|
||||||
|
|
@ -299,15 +329,18 @@ export const borrow = async (
|
||||||
|
|
||||||
const {reserveData: reserveDataBefore, userData: userDataBefore} = await getContractsData(
|
const {reserveData: reserveDataBefore, userData: userDataBefore} = await getContractsData(
|
||||||
reserve,
|
reserve,
|
||||||
user.address,
|
onBehalfOf,
|
||||||
testEnv
|
testEnv,
|
||||||
|
user.address
|
||||||
);
|
);
|
||||||
|
|
||||||
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.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0')
|
await pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf)
|
||||||
);
|
);
|
||||||
|
|
||||||
const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult);
|
const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult);
|
||||||
|
|
@ -322,7 +355,7 @@ export const borrow = async (
|
||||||
reserveData: reserveDataAfter,
|
reserveData: reserveDataAfter,
|
||||||
userData: userDataAfter,
|
userData: userDataAfter,
|
||||||
timestamp,
|
timestamp,
|
||||||
} = await getContractsData(reserve, user.address, testEnv);
|
} = await getContractsData(reserve, onBehalfOf, testEnv, user.address);
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterBorrow(
|
const expectedReserveData = calcExpectedReserveDataAfterBorrow(
|
||||||
amountToBorrow.toString(),
|
amountToBorrow.toString(),
|
||||||
|
|
@ -369,7 +402,7 @@ export const borrow = async (
|
||||||
// });
|
// });
|
||||||
} else if (expectedResult === 'revert') {
|
} else if (expectedResult === 'revert') {
|
||||||
await expect(
|
await expect(
|
||||||
pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0'),
|
pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf),
|
||||||
revertMessage
|
revertMessage
|
||||||
).to.be.reverted;
|
).to.be.reverted;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import {
|
||||||
repay,
|
repay,
|
||||||
setUseAsCollateral,
|
setUseAsCollateral,
|
||||||
swapBorrowRateMode,
|
swapBorrowRateMode,
|
||||||
rebalanceStableBorrowRate
|
rebalanceStableBorrowRate,
|
||||||
|
delegateBorrowAllowance,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {RateMode} from '../../helpers/types';
|
import {RateMode} from '../../helpers/types';
|
||||||
|
|
||||||
|
|
@ -59,7 +60,7 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
||||||
|
|
||||||
if (borrowRateMode) {
|
if (borrowRateMode) {
|
||||||
if (borrowRateMode === 'none') {
|
if (borrowRateMode === 'none') {
|
||||||
RateMode.None;
|
rateMode = RateMode.None;
|
||||||
} else if (borrowRateMode === 'stable') {
|
} else if (borrowRateMode === 'stable') {
|
||||||
rateMode = RateMode.Stable;
|
rateMode = RateMode.Stable;
|
||||||
} else if (borrowRateMode === 'variable') {
|
} else if (borrowRateMode === 'variable') {
|
||||||
|
|
@ -111,6 +112,27 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'delegateBorrowAllowance':
|
||||||
|
{
|
||||||
|
const {amount, toUser: toUserIndex} = action.args;
|
||||||
|
const toUser = users[parseInt(toUserIndex, 10)].address;
|
||||||
|
if (!amount || amount === '') {
|
||||||
|
throw `Invalid amount to deposit into the ${reserve} reserve`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await delegateBorrowAllowance(
|
||||||
|
reserve,
|
||||||
|
amount,
|
||||||
|
rateMode,
|
||||||
|
user,
|
||||||
|
toUser,
|
||||||
|
expected,
|
||||||
|
testEnv,
|
||||||
|
revertMessage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'withdraw':
|
case 'withdraw':
|
||||||
{
|
{
|
||||||
const {amount} = action.args;
|
const {amount} = action.args;
|
||||||
|
|
@ -124,13 +146,27 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
||||||
break;
|
break;
|
||||||
case 'borrow':
|
case 'borrow':
|
||||||
{
|
{
|
||||||
const {amount, timeTravel} = action.args;
|
const {amount, timeTravel, onBehalfOf: onBehalfOfIndex} = action.args;
|
||||||
|
|
||||||
|
const onBehalfOf = onBehalfOfIndex
|
||||||
|
? users[parseInt(onBehalfOfIndex)].address
|
||||||
|
: user.address;
|
||||||
|
|
||||||
if (!amount || amount === '') {
|
if (!amount || amount === '') {
|
||||||
throw `Invalid amount to borrow from the ${reserve} reserve`;
|
throw `Invalid amount to borrow from the ${reserve} reserve`;
|
||||||
}
|
}
|
||||||
|
|
||||||
await borrow(reserve, amount, rateMode, user, timeTravel, expected, testEnv, revertMessage);
|
await borrow(
|
||||||
|
reserve,
|
||||||
|
amount,
|
||||||
|
rateMode,
|
||||||
|
user,
|
||||||
|
onBehalfOf,
|
||||||
|
timeTravel,
|
||||||
|
expected,
|
||||||
|
testEnv,
|
||||||
|
revertMessage
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
148
test/helpers/scenarios/credit-delegation.json
Normal file
148
test/helpers/scenarios/credit-delegation.json
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
{
|
||||||
|
"title": "LendingPool: credit delegation",
|
||||||
|
"description": "Test cases for the credit delegation related functions.",
|
||||||
|
"stories": [
|
||||||
|
{
|
||||||
|
"description": "User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH on variable to user 4, user 4 borrows 1 WETH variable on behalf of user 0",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"name": "mint",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1000",
|
||||||
|
"user": "0"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "approve",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"user": "0"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deposit",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1000",
|
||||||
|
"user": "0"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "delegateBorrowAllowance",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "2",
|
||||||
|
"user": "0",
|
||||||
|
"borrowRateMode": "variable",
|
||||||
|
"toUser": "4"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "borrow",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1",
|
||||||
|
"user": "4",
|
||||||
|
"onBehalfOf": "0",
|
||||||
|
"borrowRateMode": "variable"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "User 4 trying to borrow 1 WETH stable on behalf of user 0, revert expected",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"name": "borrow",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1",
|
||||||
|
"user": "4",
|
||||||
|
"onBehalfOf": "0",
|
||||||
|
"borrowRateMode": "stable"
|
||||||
|
},
|
||||||
|
"expected": "revert",
|
||||||
|
"revertMessage": "54"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 3 WETH variable on behalf of user 0, revert expected",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"name": "delegateBorrowAllowance",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1",
|
||||||
|
"user": "0",
|
||||||
|
"borrowRateMode": "variable",
|
||||||
|
"toUser": "4"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "borrow",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "3",
|
||||||
|
"user": "4",
|
||||||
|
"onBehalfOf": "0",
|
||||||
|
"borrowRateMode": "variable"
|
||||||
|
},
|
||||||
|
"expected": "revert",
|
||||||
|
"revertMessage": "54"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "User 0 delegates borrowing of 1 WETH on stable to user 2, user 2 borrows 1 WETH stable on behalf of user 0",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"name": "delegateBorrowAllowance",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1",
|
||||||
|
"user": "0",
|
||||||
|
"borrowRateMode": "stable",
|
||||||
|
"toUser": "2"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "borrow",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1",
|
||||||
|
"user": "2",
|
||||||
|
"onBehalfOf": "0",
|
||||||
|
"borrowRateMode": "stable"
|
||||||
|
},
|
||||||
|
"expected": "success"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "User 0 delegates borrowing of 1 WETH to user 2 with wrong borrowRateMode, revert expected",
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"name": "delegateBorrowAllowance",
|
||||||
|
"args": {
|
||||||
|
"reserve": "WETH",
|
||||||
|
"amount": "1",
|
||||||
|
"user": "0",
|
||||||
|
"borrowRateMode": "random",
|
||||||
|
"toUser": "2"
|
||||||
|
},
|
||||||
|
"expected": "revert",
|
||||||
|
"revertMessage": "8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -933,17 +933,6 @@ const calcExpectedATokenBalance = (
|
||||||
return scaledBalanceBeforeAction.rayMul(index);
|
return scaledBalanceBeforeAction.rayMul(index);
|
||||||
};
|
};
|
||||||
|
|
||||||
const calcExpectedRedirectedBalance = (
|
|
||||||
expectedUserDataAfterAction: UserReserveData,
|
|
||||||
index: BigNumber,
|
|
||||||
redirectedBalanceBefore: BigNumber,
|
|
||||||
amountToAdd: BigNumber,
|
|
||||||
amountToSubstract: BigNumber
|
|
||||||
): BigNumber => {
|
|
||||||
return expectedUserDataAfterAction.interestRedirectionAddress !== ZERO_ADDRESS
|
|
||||||
? redirectedBalanceBefore.plus(amountToAdd.rayDiv(index)).minus(amountToSubstract.rayDiv(index))
|
|
||||||
: new BigNumber('0');
|
|
||||||
};
|
|
||||||
const calcExpectedAverageStableBorrowRate = (
|
const calcExpectedAverageStableBorrowRate = (
|
||||||
avgStableRateBefore: BigNumber,
|
avgStableRateBefore: BigNumber,
|
||||||
totalBorrowsStableBefore: BigNumber,
|
totalBorrowsStableBefore: BigNumber,
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0');
|
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address);
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
||||||
|
|
||||||
|
|
@ -261,7 +261,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
|
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
|
||||||
|
|
||||||
//drops HF below 1
|
//drops HF below 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0');
|
.borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address);
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
||||||
|
|
||||||
|
|
@ -261,7 +261,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
|
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
|
||||||
|
|
||||||
//drops HF below 1
|
//drops HF below 1
|
||||||
await oracle.setAssetPrice(
|
await oracle.setAssetPrice(
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(dai.address, amountToBorrow, 2, 0);
|
await pool.connect(user.signer).borrow(dai.address, amountToBorrow, 2, 0, user.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
||||||
|
|
@ -225,7 +225,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('User 3 repays completely his USDC loan by swapping his WETH collateral', async () => {
|
it('User 3 repays completely his USDC loan by swapping his WETH collateral', async () => {
|
||||||
|
|
@ -347,9 +347,9 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowStable, 1, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowStable, 1, 0, user.address);
|
||||||
|
|
||||||
const amountToRepay = parseUnits('80', 6);
|
const amountToRepay = parseUnits('80', 6);
|
||||||
|
|
||||||
|
|
@ -488,7 +488,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||||
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
||||||
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(dai.address, amountToBorrowVariable, 2, 0);
|
await pool.connect(user.signer).borrow(dai.address, amountToBorrowVariable, 2, 0, user.address);
|
||||||
|
|
||||||
const amountToRepay = parseEther('80');
|
const amountToRepay = parseEther('80');
|
||||||
|
|
||||||
|
|
@ -580,7 +580,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
||||||
|
|
||||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
|
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('User 5 tries to repay his USDC loan by swapping his WETH collateral, should not revert even with WETH collateral disabled', async () => {
|
it('User 5 tries to repay his USDC loan by swapping his WETH collateral, should not revert even with WETH collateral disabled', async () => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user