mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'master' into fix/114
This commit is contained in:
commit
57e444714d
|
@ -33,13 +33,6 @@ interface ILendingPool {
|
||||||
**/
|
**/
|
||||||
event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
|
event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
|
||||||
|
|
||||||
event BorrowAllowanceDelegated(
|
|
||||||
address indexed fromUser,
|
|
||||||
address indexed toUser,
|
|
||||||
address[] assets,
|
|
||||||
uint256[] interestRateModes,
|
|
||||||
uint256[] amounts
|
|
||||||
);
|
|
||||||
/**
|
/**
|
||||||
* @dev emitted on borrow
|
* @dev emitted on borrow
|
||||||
* @param reserve the address of the reserve
|
* @param reserve the address of the reserve
|
||||||
|
@ -196,27 +189,6 @@ interface ILendingPool {
|
||||||
address to
|
address to
|
||||||
) external;
|
) external;
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Sets allowance to borrow on a certain type of debt assets for a certain user address
|
|
||||||
* @param assets The underlying asset of each debt token
|
|
||||||
* @param user The user to give allowance to
|
|
||||||
* @param interestRateModes Types of debt: 1 for stable, 2 for variable
|
|
||||||
* @param amounts Allowance amounts to borrow
|
|
||||||
**/
|
|
||||||
function delegateBorrowAllowance(
|
|
||||||
address[] calldata assets,
|
|
||||||
address user,
|
|
||||||
uint256[] calldata interestRateModes,
|
|
||||||
uint256[] calldata amounts
|
|
||||||
) 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.
|
||||||
|
|
|
@ -166,53 +166,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
emit Withdraw(asset, msg.sender, to, amountToWithdraw);
|
emit Withdraw(asset, msg.sender, to, amountToWithdraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev returns the borrow allowance of the user
|
|
||||||
* @param asset The underlying asset of the debt token
|
|
||||||
* @param fromUser The user to giving allowance
|
|
||||||
* @param toUser The user to give allowance to
|
|
||||||
* @param interestRateMode Type of debt: 1 for stable, 2 for variable
|
|
||||||
* @return the current allowance of toUser
|
|
||||||
**/
|
|
||||||
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 assets for a certain user address
|
|
||||||
* @param assets The underlying asset of each debt token
|
|
||||||
* @param user The user to give allowance to
|
|
||||||
* @param interestRateModes Types of debt: 1 for stable, 2 for variable
|
|
||||||
* @param amounts Allowance amounts to borrow
|
|
||||||
**/
|
|
||||||
function delegateBorrowAllowance(
|
|
||||||
address[] calldata assets,
|
|
||||||
address user,
|
|
||||||
uint256[] calldata interestRateModes,
|
|
||||||
uint256[] calldata amounts
|
|
||||||
) external override {
|
|
||||||
_whenNotPaused();
|
|
||||||
|
|
||||||
uint256 countAssets = assets.length;
|
|
||||||
require(
|
|
||||||
countAssets == interestRateModes.length && countAssets == amounts.length,
|
|
||||||
Errors.LP_INCONSISTENT_PARAMS_LENGTH
|
|
||||||
);
|
|
||||||
|
|
||||||
for (uint256 i = 0; i < countAssets; i++) {
|
|
||||||
address debtToken = _reserves[assets[i]].getDebtTokenAddress(interestRateModes[i]);
|
|
||||||
_borrowAllowance[debtToken][msg.sender][user] = amounts[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
emit BorrowAllowanceDelegated(msg.sender, user, assets, interestRateModes, amounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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.
|
||||||
|
@ -338,6 +291,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
//burn stable rate tokens, mint variable rate tokens
|
//burn stable rate tokens, mint variable rate tokens
|
||||||
IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt);
|
IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt);
|
||||||
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
|
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
|
||||||
|
msg.sender,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
stableDebt,
|
stableDebt,
|
||||||
reserve.variableBorrowIndex
|
reserve.variableBorrowIndex
|
||||||
|
@ -350,6 +304,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
reserve.variableBorrowIndex
|
reserve.variableBorrowIndex
|
||||||
);
|
);
|
||||||
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
||||||
|
msg.sender,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
variableDebt,
|
variableDebt,
|
||||||
reserve.currentStableBorrowRate
|
reserve.currentStableBorrowRate
|
||||||
|
@ -411,6 +366,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
|
|
||||||
IStableDebtToken(address(stableDebtToken)).burn(user, stableBorrowBalance);
|
IStableDebtToken(address(stableDebtToken)).burn(user, stableBorrowBalance);
|
||||||
IStableDebtToken(address(stableDebtToken)).mint(
|
IStableDebtToken(address(stableDebtToken)).mint(
|
||||||
|
user,
|
||||||
user,
|
user,
|
||||||
stableBorrowBalance,
|
stableBorrowBalance,
|
||||||
reserve.currentStableBorrowRate
|
reserve.currentStableBorrowRate
|
||||||
|
@ -898,14 +854,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
oracle
|
oracle
|
||||||
);
|
);
|
||||||
|
|
||||||
if (vars.onBehalfOf != msg.sender) {
|
|
||||||
address debtToken = reserve.getDebtTokenAddress(vars.interestRateMode);
|
|
||||||
|
|
||||||
_borrowAllowance[debtToken][vars.onBehalfOf][msg.sender] = _borrowAllowance[debtToken][vars
|
|
||||||
.onBehalfOf][msg.sender]
|
|
||||||
.sub(vars.amount, Errors.LP_BORROW_ALLOWANCE_NOT_ENOUGH);
|
|
||||||
}
|
|
||||||
|
|
||||||
reserve.updateState();
|
reserve.updateState();
|
||||||
|
|
||||||
//caching the current stable borrow rate
|
//caching the current stable borrow rate
|
||||||
|
@ -918,12 +866,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
currentStableRate = reserve.currentStableBorrowRate;
|
currentStableRate = reserve.currentStableBorrowRate;
|
||||||
|
|
||||||
isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
||||||
|
vars.user,
|
||||||
vars.onBehalfOf,
|
vars.onBehalfOf,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
currentStableRate
|
currentStableRate
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
|
isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
|
||||||
|
vars.user,
|
||||||
vars.onBehalfOf,
|
vars.onBehalfOf,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
reserve.variableBorrowIndex
|
reserve.variableBorrowIndex
|
||||||
|
|
|
@ -15,8 +15,6 @@ contract LendingPoolStorage {
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// the list of the available reserves, structured as a mapping for gas savings reasons
|
// the list of the available reserves, structured as a mapping for gas savings reasons
|
||||||
mapping(uint256 => address) internal _reservesList;
|
mapping(uint256 => address) internal _reservesList;
|
||||||
|
|
|
@ -19,6 +19,7 @@ pragma solidity ^0.6.8;
|
||||||
library Errors {
|
library Errors {
|
||||||
//common errors
|
//common errors
|
||||||
string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin'
|
string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin'
|
||||||
|
string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small
|
||||||
|
|
||||||
//contract specific errors
|
//contract specific errors
|
||||||
string public constant VL_AMOUNT_NOT_GREATER_THAN_0 = '1'; // 'Amount must be greater than 0'
|
string public constant VL_AMOUNT_NOT_GREATER_THAN_0 = '1'; // 'Amount must be greater than 0'
|
||||||
|
@ -80,7 +81,6 @@ library Errors {
|
||||||
string public constant AT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint
|
string public constant AT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint
|
||||||
string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57';
|
string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57';
|
||||||
string public constant AT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn
|
string public constant AT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn
|
||||||
string public constant LP_BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small
|
|
||||||
string public constant LP_FAILED_COLLATERAL_SWAP = '60';
|
string public constant LP_FAILED_COLLATERAL_SWAP = '60';
|
||||||
string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61';
|
string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61';
|
||||||
string public constant LP_REENTRANCY_NOT_ALLOWED = '62';
|
string public constant LP_REENTRANCY_NOT_ALLOWED = '62';
|
||||||
|
|
|
@ -119,28 +119,6 @@ library ReserveLogic {
|
||||||
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)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (address)
|
|
||||||
{
|
|
||||||
require(
|
|
||||||
ReserveLogic.InterestRateMode.STABLE == ReserveLogic.InterestRateMode(interestRateMode) ||
|
|
||||||
ReserveLogic.InterestRateMode.VARIABLE == ReserveLogic.InterestRateMode(interestRateMode),
|
|
||||||
Errors.VL_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.
|
||||||
|
|
|
@ -16,9 +16,10 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
||||||
|
|
||||||
uint256 public constant DEBT_TOKEN_REVISION = 0x1;
|
uint256 public constant DEBT_TOKEN_REVISION = 0x1;
|
||||||
|
|
||||||
uint256 private _avgStableRate;
|
uint256 internal _avgStableRate;
|
||||||
mapping(address => uint40) _timestamps;
|
mapping(address => uint40) internal _timestamps;
|
||||||
uint40 _totalSupplyTimestamp;
|
mapping(address => uint256) internal _usersData;
|
||||||
|
uint40 internal _totalSupplyTimestamp;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
address pool,
|
address pool,
|
||||||
|
@ -95,17 +96,18 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
||||||
**/
|
**/
|
||||||
function mint(
|
function mint(
|
||||||
address user,
|
address user,
|
||||||
|
address onBehalfOf,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 rate
|
uint256 rate
|
||||||
) external override onlyLendingPool returns (bool) {
|
) external override onlyLendingPool returns (bool) {
|
||||||
MintLocalVars memory vars;
|
MintLocalVars memory vars;
|
||||||
|
|
||||||
|
if (user != onBehalfOf) {
|
||||||
|
_decreaseBorrowAllowance(onBehalfOf, user, amount);
|
||||||
|
}
|
||||||
|
|
||||||
//cumulates the user debt
|
//cumulates the user debt
|
||||||
(
|
(, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(onBehalfOf);
|
||||||
uint256 previousBalance,
|
|
||||||
uint256 currentBalance,
|
|
||||||
uint256 balanceIncrease
|
|
||||||
) = _calculateBalanceIncrease(user);
|
|
||||||
|
|
||||||
//accrueing the interest accumulation to the stored total supply and caching it
|
//accrueing the interest accumulation to the stored total supply and caching it
|
||||||
vars.previousSupply = totalSupply();
|
vars.previousSupply = totalSupply();
|
||||||
|
@ -115,17 +117,17 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
||||||
vars.amountInRay = amount.wadToRay();
|
vars.amountInRay = amount.wadToRay();
|
||||||
|
|
||||||
//calculates the new stable rate for the user
|
//calculates the new stable rate for the user
|
||||||
vars.newStableRate = _usersData[user]
|
vars.newStableRate = _usersData[onBehalfOf]
|
||||||
.rayMul(currentBalance.wadToRay())
|
.rayMul(currentBalance.wadToRay())
|
||||||
.add(vars.amountInRay.rayMul(rate))
|
.add(vars.amountInRay.rayMul(rate))
|
||||||
.rayDiv(currentBalance.add(amount).wadToRay());
|
.rayDiv(currentBalance.add(amount).wadToRay());
|
||||||
|
|
||||||
require(vars.newStableRate < (1 << 128), 'Debt token: stable rate overflow');
|
require(vars.newStableRate < (1 << 128), 'Debt token: stable rate overflow');
|
||||||
_usersData[user] = vars.newStableRate;
|
_usersData[onBehalfOf] = vars.newStableRate;
|
||||||
|
|
||||||
//updating the user and supply timestamp
|
//updating the user and supply timestamp
|
||||||
//solium-disable-next-line
|
//solium-disable-next-line
|
||||||
_totalSupplyTimestamp = _timestamps[user] = uint40(block.timestamp);
|
_totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp);
|
||||||
|
|
||||||
//calculates the updated average stable rate
|
//calculates the updated average stable rate
|
||||||
vars.currentAvgStableRate = _avgStableRate = vars
|
vars.currentAvgStableRate = _avgStableRate = vars
|
||||||
|
@ -134,13 +136,14 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
||||||
.add(rate.rayMul(vars.amountInRay))
|
.add(rate.rayMul(vars.amountInRay))
|
||||||
.rayDiv(vars.nextSupply.wadToRay());
|
.rayDiv(vars.nextSupply.wadToRay());
|
||||||
|
|
||||||
_mint(user, amount.add(balanceIncrease), vars.previousSupply);
|
_mint(onBehalfOf, amount.add(balanceIncrease), vars.previousSupply);
|
||||||
|
|
||||||
// transfer event to track balances
|
// transfer event to track balances
|
||||||
emit Transfer(address(0), user, amount);
|
emit Transfer(address(0), onBehalfOf, amount);
|
||||||
|
|
||||||
emit Mint(
|
emit Mint(
|
||||||
user,
|
user,
|
||||||
|
onBehalfOf,
|
||||||
amount,
|
amount,
|
||||||
currentBalance,
|
currentBalance,
|
||||||
balanceIncrease,
|
balanceIncrease,
|
||||||
|
@ -158,11 +161,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
||||||
* @param amount the amount of debt tokens to mint
|
* @param amount the amount of debt tokens to mint
|
||||||
**/
|
**/
|
||||||
function burn(address user, uint256 amount) external override onlyLendingPool {
|
function burn(address user, uint256 amount) external override onlyLendingPool {
|
||||||
(
|
(, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(user);
|
||||||
uint256 previousBalance,
|
|
||||||
uint256 currentBalance,
|
|
||||||
uint256 balanceIncrease
|
|
||||||
) = _calculateBalanceIncrease(user);
|
|
||||||
|
|
||||||
uint256 previousSupply = totalSupply();
|
uint256 previousSupply = totalSupply();
|
||||||
uint256 newStableRate = 0;
|
uint256 newStableRate = 0;
|
||||||
|
|
|
@ -55,17 +55,22 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
|
||||||
**/
|
**/
|
||||||
function mint(
|
function mint(
|
||||||
address user,
|
address user,
|
||||||
|
address onBehalfOf,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 index
|
uint256 index
|
||||||
) external override onlyLendingPool returns (bool) {
|
) external override onlyLendingPool returns (bool) {
|
||||||
uint256 previousBalance = super.balanceOf(user);
|
if (user != onBehalfOf) {
|
||||||
|
_decreaseBorrowAllowance(onBehalfOf, user, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 previousBalance = super.balanceOf(onBehalfOf);
|
||||||
uint256 amountScaled = amount.rayDiv(index);
|
uint256 amountScaled = amount.rayDiv(index);
|
||||||
require(amountScaled != 0, Errors.AT_INVALID_MINT_AMOUNT);
|
require(amountScaled != 0, Errors.AT_INVALID_MINT_AMOUNT);
|
||||||
|
|
||||||
_mint(user, amountScaled);
|
_mint(onBehalfOf, amountScaled);
|
||||||
|
|
||||||
emit Transfer(address(0), user, amount);
|
emit Transfer(address(0), onBehalfOf, amount);
|
||||||
emit Mint(user, amount, index);
|
emit Mint(user, onBehalfOf, amount, index);
|
||||||
|
|
||||||
return previousBalance == 0;
|
return previousBalance == 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,17 @@ import {Errors} from '../../libraries/helpers/Errors.sol';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable {
|
abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable {
|
||||||
|
event BorrowAllowanceDelegated(
|
||||||
|
address indexed fromUser,
|
||||||
|
address indexed toUser,
|
||||||
|
address asset,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
address public immutable UNDERLYING_ASSET_ADDRESS;
|
address public immutable UNDERLYING_ASSET_ADDRESS;
|
||||||
ILendingPool public immutable POOL;
|
ILendingPool public immutable POOL;
|
||||||
mapping(address => uint256) internal _usersData;
|
|
||||||
|
mapping(address => mapping(address => uint256)) internal _borrowAllowances;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Only lending pool can call functions marked by this modifier
|
* @dev Only lending pool can call functions marked by this modifier
|
||||||
|
@ -58,6 +66,28 @@ abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable {
|
||||||
_setDecimals(decimals);
|
_setDecimals(decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev delegates borrowing power to a user on the specific debt token
|
||||||
|
* @param delegatee the address receiving the delegated borrowing power
|
||||||
|
* @param amount the maximum amount being delegated. Delegation will still
|
||||||
|
* respect the liquidation constraints (even if delegated, a delegatee cannot
|
||||||
|
* force a delegator HF to go below 1)
|
||||||
|
**/
|
||||||
|
function approveDelegation(address delegatee, uint256 amount) external {
|
||||||
|
_borrowAllowances[_msgSender()][delegatee] = amount;
|
||||||
|
emit BorrowAllowanceDelegated(_msgSender(), delegatee, UNDERLYING_ASSET_ADDRESS, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns the borrow allowance of the user
|
||||||
|
* @param fromUser The user to giving allowance
|
||||||
|
* @param toUser The user to give allowance to
|
||||||
|
* @return the current allowance of toUser
|
||||||
|
**/
|
||||||
|
function borrowAllowance(address fromUser, address toUser) external view returns (uint256) {
|
||||||
|
return _borrowAllowances[fromUser][toUser];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Being non transferrable, the debt token does not implement any of the
|
* @dev Being non transferrable, the debt token does not implement any of the
|
||||||
* standard ERC20 functions for transfer and allowance.
|
* standard ERC20 functions for transfer and allowance.
|
||||||
|
@ -118,4 +148,19 @@ abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable {
|
||||||
subtractedValue;
|
subtractedValue;
|
||||||
revert('ALLOWANCE_NOT_SUPPORTED');
|
revert('ALLOWANCE_NOT_SUPPORTED');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _decreaseBorrowAllowance(
|
||||||
|
address delegator,
|
||||||
|
address delegatee,
|
||||||
|
uint256 amount
|
||||||
|
) internal {
|
||||||
|
uint256 newAllowance = _borrowAllowances[delegator][delegatee].sub(
|
||||||
|
amount,
|
||||||
|
Errors.BORROW_ALLOWANCE_NOT_ENOUGH
|
||||||
|
);
|
||||||
|
|
||||||
|
_borrowAllowances[delegator][delegatee] = newAllowance;
|
||||||
|
|
||||||
|
emit BorrowAllowanceDelegated(delegator, delegatee, UNDERLYING_ASSET_ADDRESS, newAllowance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,27 @@ import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||||
import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
|
import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
|
||||||
|
|
||||||
interface IAToken is IERC20, IScaledBalanceToken {
|
interface IAToken is IERC20, IScaledBalanceToken {
|
||||||
|
/**
|
||||||
|
* @dev emitted after the mint action
|
||||||
|
* @param from the address performing the mint
|
||||||
|
* @param value the amount to be minted
|
||||||
|
* @param index the last index of the reserve
|
||||||
|
**/
|
||||||
|
event Mint(address indexed from, uint256 value, uint256 index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev mints aTokens to user
|
||||||
|
* only lending pools can call this function
|
||||||
|
* @param user the address receiving the minted tokens
|
||||||
|
* @param amount the amount of tokens to mint
|
||||||
|
* @param index the liquidity index
|
||||||
|
*/
|
||||||
|
function mint(
|
||||||
|
address user,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 index
|
||||||
|
) external returns (bool);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev emitted after aTokens are burned
|
* @dev emitted after aTokens are burned
|
||||||
* @param from the address performing the redeem
|
* @param from the address performing the redeem
|
||||||
|
|
|
@ -2,27 +2,6 @@
|
||||||
pragma solidity ^0.6.8;
|
pragma solidity ^0.6.8;
|
||||||
|
|
||||||
interface IScaledBalanceToken {
|
interface IScaledBalanceToken {
|
||||||
/**
|
|
||||||
* @dev emitted after the mint action
|
|
||||||
* @param from the address performing the mint
|
|
||||||
* @param value the amount to be minted
|
|
||||||
* @param index the last index of the reserve
|
|
||||||
**/
|
|
||||||
event Mint(address indexed from, uint256 value, uint256 index);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev mints aTokens to user
|
|
||||||
* only lending pools can call this function
|
|
||||||
* @param user the address receiving the minted tokens
|
|
||||||
* @param amount the amount of tokens to mint
|
|
||||||
* @param index the liquidity index
|
|
||||||
*/
|
|
||||||
function mint(
|
|
||||||
address user,
|
|
||||||
uint256 amount,
|
|
||||||
uint256 index
|
|
||||||
) external returns (bool);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev returns the principal balance of the user. The principal balance is the last
|
* @dev returns the principal balance of the user. The principal balance is the last
|
||||||
* updated stored balance, which does not consider the perpetually accruing interest.
|
* updated stored balance, which does not consider the perpetually accruing interest.
|
||||||
|
|
|
@ -15,7 +15,8 @@ pragma solidity ^0.6.8;
|
||||||
interface IStableDebtToken {
|
interface IStableDebtToken {
|
||||||
/**
|
/**
|
||||||
* @dev emitted when new stable debt is minted
|
* @dev emitted when new stable debt is minted
|
||||||
* @param user the address of the user
|
* @param user the address of the user who triggered the minting
|
||||||
|
* @param onBehalfOf the address of the user
|
||||||
* @param amount the amount minted
|
* @param amount the amount minted
|
||||||
* @param currentBalance the current balance of the user
|
* @param currentBalance the current balance of the user
|
||||||
* @param balanceIncrease the the increase in balance since the last action of the user
|
* @param balanceIncrease the the increase in balance since the last action of the user
|
||||||
|
@ -25,6 +26,7 @@ interface IStableDebtToken {
|
||||||
**/
|
**/
|
||||||
event Mint(
|
event Mint(
|
||||||
address indexed user,
|
address indexed user,
|
||||||
|
address indexed onBehalfOf,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 currentBalance,
|
uint256 currentBalance,
|
||||||
uint256 balanceIncrease,
|
uint256 balanceIncrease,
|
||||||
|
@ -60,6 +62,7 @@ interface IStableDebtToken {
|
||||||
**/
|
**/
|
||||||
function mint(
|
function mint(
|
||||||
address user,
|
address user,
|
||||||
|
address onBehalfOf,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 rate
|
uint256 rate
|
||||||
) external returns (bool);
|
) external returns (bool);
|
||||||
|
|
|
@ -9,6 +9,29 @@ import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
|
||||||
* @notice defines the basic interface for a variable debt token.
|
* @notice defines the basic interface for a variable debt token.
|
||||||
**/
|
**/
|
||||||
interface IVariableDebtToken is IScaledBalanceToken {
|
interface IVariableDebtToken is IScaledBalanceToken {
|
||||||
|
/**
|
||||||
|
* @dev emitted after the mint action
|
||||||
|
* @param from the address performing the mint
|
||||||
|
* @param onBehalfOf the address of the user on which behalf minting has been performed
|
||||||
|
* @param value the amount to be minted
|
||||||
|
* @param index the last index of the reserve
|
||||||
|
**/
|
||||||
|
event Mint(address indexed from, address indexed onBehalfOf, uint256 value, uint256 index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev mints aTokens to user
|
||||||
|
* only lending pools can call this function
|
||||||
|
* @param user the address receiving the minted tokens
|
||||||
|
* @param amount the amount of tokens to mint
|
||||||
|
* @param index the liquidity index
|
||||||
|
*/
|
||||||
|
function mint(
|
||||||
|
address user,
|
||||||
|
address onBehalfOf,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 index
|
||||||
|
) external returns (bool);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev emitted when variable debt is burnt
|
* @dev emitted when variable debt is burnt
|
||||||
* @param user the user which debt has been burned
|
* @param user the user which debt has been burned
|
||||||
|
|
|
@ -8,6 +8,9 @@ import {UserConfiguration} from '../../contracts/libraries/configuration/UserCon
|
||||||
import {ReserveLogic} from '../../contracts/libraries/logic/ReserveLogic.sol';
|
import {ReserveLogic} from '../../contracts/libraries/logic/ReserveLogic.sol';
|
||||||
import {ILendingPool} from '../../contracts/interfaces/ILendingPool.sol';
|
import {ILendingPool} from '../../contracts/interfaces/ILendingPool.sol';
|
||||||
import {LendingPool} from '../../contracts/lendingpool/LendingPool.sol';
|
import {LendingPool} from '../../contracts/lendingpool/LendingPool.sol';
|
||||||
|
import {
|
||||||
|
ILendingPoolAddressesProvider
|
||||||
|
} from '../../contracts/interfaces/ILendingPoolAddressesProvider.sol';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Certora: Harness that delegates calls to the original LendingPool.
|
Certora: Harness that delegates calls to the original LendingPool.
|
||||||
|
@ -25,26 +28,12 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
|
||||||
originalPool.deposit(asset, amount, onBehalfOf, referralCode);
|
originalPool.deposit(asset, amount, onBehalfOf, referralCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function withdraw(address asset, uint256 amount) external override {
|
function withdraw(
|
||||||
originalPool.withdraw(asset, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBorrowAllowance(
|
|
||||||
address fromUser,
|
|
||||||
address toUser,
|
|
||||||
address asset,
|
address asset,
|
||||||
uint256 interestRateMode
|
uint256 amount,
|
||||||
) external override view returns (uint256) {
|
address to
|
||||||
return originalPool.getBorrowAllowance(fromUser, toUser, asset, interestRateMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
function delegateBorrowAllowance(
|
|
||||||
address asset,
|
|
||||||
address user,
|
|
||||||
uint256 interestRateMode,
|
|
||||||
uint256 amount
|
|
||||||
) external override {
|
) external override {
|
||||||
originalPool.delegateBorrowAllowance(asset, user, interestRateMode, amount);
|
originalPool.withdraw(asset, amount, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
function borrow(
|
function borrow(
|
||||||
|
@ -193,12 +182,12 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
|
||||||
address receiver,
|
address receiver,
|
||||||
address[] calldata assets,
|
address[] calldata assets,
|
||||||
uint256[] calldata amounts,
|
uint256[] calldata amounts,
|
||||||
uint256 mode,
|
uint256[] calldata modes,
|
||||||
address onBehalfOf,
|
address onBehalfOf,
|
||||||
bytes calldata params,
|
bytes calldata params,
|
||||||
uint16 referralCode
|
uint16 referralCode
|
||||||
) external override {
|
) external override {
|
||||||
originalPool.flashLoan(receiver, assets, amounts, mode, onBehalfOf, params, referralCode);
|
originalPool.flashLoan(receiver, assets, amounts, modes, onBehalfOf, params, referralCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function finalizeTransfer(
|
function finalizeTransfer(
|
||||||
|
@ -211,4 +200,8 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
|
||||||
) external override {
|
) external override {
|
||||||
originalPool.finalizeTransfer(asset, from, to, amount, balanceFromAfter, balanceToBefore);
|
originalPool.finalizeTransfer(asset, from, to, amount, balanceFromAfter, balanceToBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAddressesProvider() external override view returns (ILendingPoolAddressesProvider) {
|
||||||
|
return originalPool.getAddressesProvider();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,10 +454,12 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
const flashAmount = ethers.utils.parseEther('0.8');
|
const flashAmount = ethers.utils.parseEther('0.8');
|
||||||
|
|
||||||
|
const reserveData = await pool.getReserveData(weth.address);
|
||||||
|
|
||||||
|
const stableDebtToken = await getVariableDebtToken(reserveData.stableDebtTokenAddress);
|
||||||
|
|
||||||
// Deposited for onBehalfOf user already, delegate borrow allowance
|
// Deposited for onBehalfOf user already, delegate borrow allowance
|
||||||
await pool
|
await stableDebtToken.connect(onBehalfOf.signer).approveDelegation(caller.address, flashAmount);
|
||||||
.connect(onBehalfOf.signer)
|
|
||||||
.delegateBorrowAllowance([weth.address], caller.address, [1], [flashAmount]);
|
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,12 @@ import {
|
||||||
import {getReserveAddressFromSymbol, getReserveData, getUserData} from './utils/helpers';
|
import {getReserveAddressFromSymbol, getReserveData, getUserData} from './utils/helpers';
|
||||||
|
|
||||||
import {convertToCurrencyDecimals} from '../../helpers/contracts-helpers';
|
import {convertToCurrencyDecimals} from '../../helpers/contracts-helpers';
|
||||||
import {getAToken, getMintableErc20} from '../../helpers/contracts-getters';
|
import {
|
||||||
|
getAToken,
|
||||||
|
getMintableErc20,
|
||||||
|
getStableDebtToken,
|
||||||
|
getVariableDebtToken,
|
||||||
|
} from '../../helpers/contracts-getters';
|
||||||
import {MAX_UINT_AMOUNT, ONE_YEAR} from '../../helpers/constants';
|
import {MAX_UINT_AMOUNT, ONE_YEAR} from '../../helpers/constants';
|
||||||
import {SignerWithAddress, TestEnv} from './make-suite';
|
import {SignerWithAddress, TestEnv} from './make-suite';
|
||||||
import {BRE, increaseTime, timeLatest, waitForTx} from '../../helpers/misc-utils';
|
import {BRE, increaseTime, timeLatest, waitForTx} from '../../helpers/misc-utils';
|
||||||
|
@ -277,9 +282,9 @@ export const withdraw = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const delegateBorrowAllowance = async (
|
export const delegateBorrowAllowance = async (
|
||||||
reserveSymbols: string[],
|
reserve: string,
|
||||||
amounts: string[],
|
amount: string,
|
||||||
interestRateModes: string[],
|
interestRateMode: string,
|
||||||
user: SignerWithAddress,
|
user: SignerWithAddress,
|
||||||
receiver: tEthereumAddress,
|
receiver: tEthereumAddress,
|
||||||
expectedResult: string,
|
expectedResult: string,
|
||||||
|
@ -288,32 +293,33 @@ export const delegateBorrowAllowance = async (
|
||||||
) => {
|
) => {
|
||||||
const {pool} = testEnv;
|
const {pool} = testEnv;
|
||||||
|
|
||||||
const reserves: tEthereumAddress[] = [];
|
const reserveAddress: tEthereumAddress = await getReserveAddressFromSymbol(reserve);
|
||||||
const amountsToDelegate: tEthereumAddress[] = [];
|
|
||||||
for (const reserveSymbol of reserveSymbols) {
|
|
||||||
const newLength = reserves.push(await getReserveAddressFromSymbol(reserveSymbol));
|
|
||||||
amountsToDelegate.push(
|
|
||||||
await (
|
|
||||||
await convertToCurrencyDecimals(reserves[newLength - 1], amounts[newLength - 1])
|
|
||||||
).toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const delegateAllowancePromise = pool
|
const amountToDelegate: string = await (
|
||||||
|
await convertToCurrencyDecimals(reserveAddress, amount)
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
const reserveData = await pool.getReserveData(reserveAddress);
|
||||||
|
|
||||||
|
const debtToken =
|
||||||
|
interestRateMode === '1'
|
||||||
|
? await getStableDebtToken(reserveData.stableDebtTokenAddress)
|
||||||
|
: await getVariableDebtToken(reserveData.variableDebtTokenAddress);
|
||||||
|
|
||||||
|
const delegateAllowancePromise = debtToken
|
||||||
.connect(user.signer)
|
.connect(user.signer)
|
||||||
.delegateBorrowAllowance(reserves, receiver, interestRateModes, amountsToDelegate);
|
.approveDelegation(receiver, amountToDelegate);
|
||||||
if (expectedResult === 'revert') {
|
|
||||||
await expect(delegateAllowancePromise, revertMessage).to.be.reverted;
|
if (expectedResult === 'revert' && revertMessage) {
|
||||||
|
await expect(delegateAllowancePromise, revertMessage).to.be.revertedWith(revertMessage);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
await delegateAllowancePromise;
|
await delegateAllowancePromise;
|
||||||
for (const [i, reserve] of reserves.entries()) {
|
const allowance = await debtToken.borrowAllowance(user.address, receiver);
|
||||||
expect(
|
expect(allowance.toString()).to.be.equal(
|
||||||
(
|
amountToDelegate,
|
||||||
await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateModes[i])
|
'borrowAllowance is set incorrectly'
|
||||||
).toString()
|
);
|
||||||
).to.be.equal(amountsToDelegate[i], 'borrowAllowance are set incorrectly');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -121,9 +121,9 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
await delegateBorrowAllowance(
|
await delegateBorrowAllowance(
|
||||||
[reserve],
|
reserve,
|
||||||
[amount],
|
amount,
|
||||||
[rateMode],
|
rateMode,
|
||||||
user,
|
user,
|
||||||
toUser,
|
toUser,
|
||||||
expected,
|
expected,
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
"borrowRateMode": "stable"
|
"borrowRateMode": "stable"
|
||||||
},
|
},
|
||||||
"expected": "revert",
|
"expected": "revert",
|
||||||
"revertMessage": "54"
|
"revertMessage": "59"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
"borrowRateMode": "variable"
|
"borrowRateMode": "variable"
|
||||||
},
|
},
|
||||||
"expected": "revert",
|
"expected": "revert",
|
||||||
"revertMessage": "54"
|
"revertMessage": "59"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -126,23 +126,6 @@
|
||||||
"expected": "success"
|
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,23 +122,6 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
await configurator.connect(users[1].signer).setPoolPause(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('DelegateBorrowAllowance', async () => {
|
|
||||||
const {pool, dai, users, configurator} = testEnv;
|
|
||||||
|
|
||||||
const user = users[1];
|
|
||||||
const toUser = users[2];
|
|
||||||
// Pause the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(true);
|
|
||||||
|
|
||||||
// Try to execute liquidation
|
|
||||||
await expect(
|
|
||||||
pool.connect(user.signer).delegateBorrowAllowance([dai.address], toUser.address, ['1'], ['1'])
|
|
||||||
).revertedWith(LP_IS_PAUSED);
|
|
||||||
|
|
||||||
// Unpause the pool
|
|
||||||
await configurator.connect(users[1].signer).setPoolPause(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Borrow', async () => {
|
it('Borrow', async () => {
|
||||||
const {pool, dai, users, configurator} = testEnv;
|
const {pool, dai, users, configurator} = testEnv;
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,9 @@ makeSuite('Stable debt token tests', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
const stableDebtContract = await getStableDebtToken(daiStableDebtTokenAddress);
|
const stableDebtContract = await getStableDebtToken(daiStableDebtTokenAddress);
|
||||||
|
|
||||||
await expect(stableDebtContract.mint(deployer.address, '1', '1')).to.be.revertedWith(
|
await expect(
|
||||||
AT_CALLER_MUST_BE_LENDING_POOL
|
stableDebtContract.mint(deployer.address, deployer.address, '1', '1')
|
||||||
);
|
).to.be.revertedWith(AT_CALLER_MUST_BE_LENDING_POOL);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Tries to invoke burn not being the LendingPool', async () => {
|
it('Tries to invoke burn not being the LendingPool', async () => {
|
||||||
|
|
|
@ -15,9 +15,9 @@ makeSuite('Variable debt token tests', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
const variableDebtContract = await getVariableDebtToken(daiVariableDebtTokenAddress);
|
const variableDebtContract = await getVariableDebtToken(daiVariableDebtTokenAddress);
|
||||||
|
|
||||||
await expect(variableDebtContract.mint(deployer.address, '1', '1')).to.be.revertedWith(
|
await expect(
|
||||||
AT_CALLER_MUST_BE_LENDING_POOL
|
variableDebtContract.mint(deployer.address, deployer.address, '1', '1')
|
||||||
);
|
).to.be.revertedWith(AT_CALLER_MUST_BE_LENDING_POOL);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Tries to invoke burn not being the LendingPool', async () => {
|
it('Tries to invoke burn not being the LendingPool', async () => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user