Merge branch 'feat/102-batch-delegation-allowance' into 'master'

Added batch to credit delegation

Closes #102

See merge request aave-tech/protocol-v2!109
This commit is contained in:
Ernesto Boado 2020-10-30 15:15:35 +00:00
commit 8e08614145
12 changed files with 163 additions and 141 deletions

View File

@ -33,11 +33,11 @@ interface ILendingPool {
event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
event BorrowAllowanceDelegated(
address indexed asset,
address indexed fromUser,
address indexed toUser,
uint256 interestRateMode,
uint256 amount
address[] assets,
uint256[] interestRateModes,
uint256[] amounts
);
/**
* @dev emitted on borrow
@ -196,17 +196,17 @@ interface ILendingPool {
) 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
* @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 interestRateMode Type of debt: 1 for stable, 2 for variable
* @param amount Allowance amount to borrow
* @param interestRateModes Types of debt: 1 for stable, 2 for variable
* @param amounts Allowance amounts to borrow
**/
function delegateBorrowAllowance(
address asset,
address[] calldata assets,
address user,
uint256 interestRateMode,
uint256 amount
uint256[] calldata interestRateModes,
uint256[] calldata amounts
) external;
function getBorrowAllowance(

View File

@ -183,23 +183,32 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
}
/**
* @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
* @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 interestRateMode Type of debt: 1 for stable, 2 for variable
* @param amount Allowance amount to borrow
* @param interestRateModes Types of debt: 1 for stable, 2 for variable
* @param amounts Allowance amounts to borrow
**/
function delegateBorrowAllowance(
address asset,
address[] calldata assets,
address user,
uint256 interestRateMode,
uint256 amount
uint256[] calldata interestRateModes,
uint256[] calldata amounts
) external override {
_whenNotPaused();
address debtToken = _reserves[asset].getDebtTokenAddress(interestRateMode);
_borrowAllowance[debtToken][msg.sender][user] = amount;
emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount);
uint256 countAssets = assets.length;
require(
countAssets == interestRateModes.length && countAssets == amounts.length,
Errors.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);
}
/**
@ -698,7 +707,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
* @param asset the address of the reserve
* @return the reserve normalized income
*/
function getReserveNormalizedIncome(address asset) external virtual override view returns (uint256) {
function getReserveNormalizedIncome(address asset)
external
virtual
override
view
returns (uint256)
{
return _reserves[asset].getNormalizedIncome();
}

View File

@ -98,6 +98,9 @@ library Errors {
string public constant INVALID_DECIMALS = '73';
string public constant INVALID_RESERVE_FACTOR = '74';
// Credit delegation
string public constant INCONSISTENT_PARAMS_LENGTH = '75';
enum CollateralManagerErrors {
NO_ERROR,
NO_COLLATERAL_AVAILABLE,

View File

@ -33,7 +33,7 @@ library ValidationLogic {
* @param reserve the reserve state on which the user is depositing
* @param amount the amount to be deposited
*/
function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) internal view {
function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) external view {
(bool isActive, bool isFreezed, , ) = reserve.configuration.getFlags();
require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0);
@ -61,7 +61,7 @@ library ValidationLogic {
mapping(uint256 => address) storage reserves,
uint256 reservesCount,
address oracle
) internal view {
) external view {
require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0);
require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);

View File

@ -140,7 +140,6 @@ export const deployAaveLibraries = async (
return {
['__$5201a97c05ba6aa659e2f36a933dd51801$__']: validationLogic.address,
['__$d3b4366daeb9cadc7528af6145b50b2183$__']: reserveLogic.address,
['__$4c26be947d349222af871a3168b3fe584b$__']: genericLogic.address,
};
};

View File

@ -1,7 +1,9 @@
pragma solidity ^0.6.8;
pragma experimental ABIEncoderV2;
import {ReserveConfiguration} from '../../contracts/libraries/configuration/ReserveConfiguration.sol';
import {
ReserveConfiguration
} from '../../contracts/libraries/configuration/ReserveConfiguration.sol';
import {UserConfiguration} from '../../contracts/libraries/configuration/UserConfiguration.sol';
import {ReserveLogic} from '../../contracts/libraries/logic/ReserveLogic.sol';
import {ILendingPool} from '../../contracts/interfaces/ILendingPool.sol';
@ -12,7 +14,6 @@ Certora: Harness that delegates calls to the original LendingPool.
Used for the verification of the VariableDebtToken contract.
*/
contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
LendingPool private originalPool;
function deposit(
@ -91,26 +92,36 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
return originalPool.getReservesList();
}
function getReserveData(address asset) external override view returns (ReserveLogic.ReserveData memory) {
function getReserveData(address asset)
external
override
view
returns (ReserveLogic.ReserveData memory)
{
return originalPool.getReserveData(asset);
}
function getUserConfiguration(address user) external override view returns (UserConfiguration.Map memory) {
function getUserConfiguration(address user)
external
override
view
returns (UserConfiguration.Map memory)
{
return originalPool.getUserConfiguration(user);
}
function getUserAccountData(address user)
external
override
view
returns (
uint256 totalCollateralETH,
uint256 totalBorrowsETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
)
external
override
view
returns (
uint256 totalCollateralETH,
uint256 totalBorrowsETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
)
{
return originalPool.getUserAccountData(user);
}
@ -122,12 +133,18 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
address variableDebtAddress,
address interestRateStrategyAddress
) external override {
originalPool.initReserve(asset, aTokenAddress, stableDebtAddress, variableDebtAddress, interestRateStrategyAddress);
originalPool.initReserve(
asset,
aTokenAddress,
stableDebtAddress,
variableDebtAddress,
interestRateStrategyAddress
);
}
function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
external
override
external
override
{
originalPool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress);
}
@ -137,10 +154,10 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
}
function getConfiguration(address asset)
external
override
view
returns (ReserveConfiguration.Map memory)
external
override
view
returns (ReserveConfiguration.Map memory)
{
return originalPool.getConfiguration(asset);
}
@ -155,10 +172,10 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
mapping(uint256 => uint256) private reserveNormalizedVariableDebt;
function getReserveNormalizedVariableDebt(address asset)
external
override
view
returns (uint256)
external
override
view
returns (uint256)
{
require(reserveNormalizedVariableDebt[block.timestamp] == 1e27);
return reserveNormalizedVariableDebt[block.timestamp];
@ -194,5 +211,4 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
) external override {
originalPool.finalizeTransfer(asset, from, to, amount, balanceFromAfter, balanceToBefore);
}
}

View File

@ -4,25 +4,23 @@ import {StableDebtToken} from '../../contracts/tokenization/StableDebtToken.sol'
import {IncentivizedERC20} from '../../contracts/tokenization/IncentivizedERC20.sol';
contract StableDebtTokenHarness is StableDebtToken {
constructor(
address pool,
address underlyingAsset,
string memory name,
string memory symbol,
address incentivesController
) public StableDebtToken(pool, underlyingAsset, name, symbol, incentivesController) {}
constructor(
address pool,
address underlyingAsset,
string memory name,
string memory symbol,
address incentivesController
) public StableDebtToken(pool, underlyingAsset, name, symbol, incentivesController) {}
function balanceOf(address account) public override view returns (uint256) {
return IncentivizedERC20.balanceOf(account);
}
function balanceOf(address account) public override view returns (uint256) {
return IncentivizedERC20.balanceOf(account);
}
function _calcTotalSupply(uint256 avgRate) internal override view returns (uint256) {
return IncentivizedERC20.totalSupply();
}
function _calcTotalSupply(uint256 avgRate) internal override view returns (uint256) {
return IncentivizedERC20.totalSupply();
}
function getIncentivesController() public view returns (address) {
return address(_incentivesController);
}
}
function getIncentivesController() public view returns (address) {
return address(_incentivesController);
}
}

View File

@ -7,71 +7,50 @@ import {UserConfiguration} from '../../contracts/libraries/configuration/UserCon
A wrapper contract for calling functions from the library UserConfiguration.
*/
contract UserConfigurationHarness {
UserConfiguration.Map internal usersConfig;
UserConfiguration.Map internal usersConfig;
function setBorrowing(
address user,
uint256 reserveIndex,
bool borrowing
) public {
UserConfiguration.setBorrowing(usersConfig, reserveIndex, borrowing);
}
function setBorrowing(
address user,
uint256 reserveIndex,
bool borrowing
) public {
UserConfiguration.setBorrowing(usersConfig, reserveIndex, borrowing);
}
function setUsingAsCollateral(
address user,
uint256 reserveIndex,
bool _usingAsCollateral
) public {
UserConfiguration.setUsingAsCollateral(usersConfig, reserveIndex, _usingAsCollateral);
}
function setUsingAsCollateral(
address user,
uint256 reserveIndex,
bool _usingAsCollateral
) public {
UserConfiguration.setUsingAsCollateral(usersConfig, reserveIndex, _usingAsCollateral);
}
function isUsingAsCollateralOrBorrowing(
address user,
uint256 reserveIndex)
public
function isUsingAsCollateralOrBorrowing(address user, uint256 reserveIndex)
public
view
returns (bool) {
return UserConfiguration.isUsingAsCollateralOrBorrowing(usersConfig, reserveIndex);
}
returns (bool)
{
return UserConfiguration.isUsingAsCollateralOrBorrowing(usersConfig, reserveIndex);
}
function isBorrowing(
address user,
uint256 reserveIndex)
public
view
returns (bool) {
return UserConfiguration.isBorrowing(usersConfig, reserveIndex);
}
function isBorrowing(address user, uint256 reserveIndex) public view returns (bool) {
return UserConfiguration.isBorrowing(usersConfig, reserveIndex);
}
function isUsingAsCollateral(
address user,
uint256 reserveIndex)
public
view
returns (bool) {
return UserConfiguration.isUsingAsCollateral(usersConfig, reserveIndex);
}
function isUsingAsCollateral(address user, uint256 reserveIndex) public view returns (bool) {
return UserConfiguration.isUsingAsCollateral(usersConfig, reserveIndex);
}
function isBorrowingAny(
address user)
public
view
returns (bool) {
return UserConfiguration.isBorrowingAny(usersConfig);
}
function isBorrowingAny(address user) public view returns (bool) {
return UserConfiguration.isBorrowingAny(usersConfig);
}
function isEmpty(
address user)
public
view
returns (bool) {
return UserConfiguration.isEmpty(usersConfig);
}
/*
function isEmpty(address user) public view returns (bool) {
return UserConfiguration.isEmpty(usersConfig);
}
/*
Mimics the original constructor of the contract.
*/
function init_state() public { }
function init_state() public {}
}

View File

@ -464,7 +464,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
// Deposited for onBehalfOf user already, delegate borrow allowance
await pool
.connect(onBehalfOf.signer)
.delegateBorrowAllowance(weth.address, caller.address, 1, flashAmount);
.delegateBorrowAllowance([weth.address], caller.address, [1], [flashAmount]);
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);

View File

@ -277,9 +277,9 @@ export const withdraw = async (
};
export const delegateBorrowAllowance = async (
reserveSymbol: string,
amount: string,
interestRateMode: string,
reserveSymbols: string[],
amounts: string[],
interestRateModes: string[],
user: SignerWithAddress,
receiver: tEthereumAddress,
expectedResult: string,
@ -288,20 +288,32 @@ export const delegateBorrowAllowance = async (
) => {
const {pool} = testEnv;
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
const amountToDelegate = await convertToCurrencyDecimals(reserve, amount);
const reserves: tEthereumAddress[] = [];
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
.connect(user.signer)
.delegateBorrowAllowance(reserve, receiver, interestRateMode, amountToDelegate.toString());
.delegateBorrowAllowance(reserves, receiver, interestRateModes, amountsToDelegate);
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');
for (const [i, reserve] of reserves.entries()) {
expect(
(
await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateModes[i])
).toString()
).to.be.equal(amountsToDelegate[i], 'borrowAllowance are set incorrectly');
}
}
};

View File

@ -121,9 +121,9 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
}
await delegateBorrowAllowance(
reserve,
amount,
rateMode,
[reserve],
[amount],
[rateMode],
user,
toUser,
expected,

View File

@ -133,7 +133,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
// Try to execute liquidation
await expect(
pool.connect(user.signer).delegateBorrowAllowance(dai.address, toUser.address, '1', '1')
pool.connect(user.signer).delegateBorrowAllowance([dai.address], toUser.address, ['1'], ['1'])
).revertedWith(IS_PAUSED);
// Unpause the pool