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 Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
event BorrowAllowanceDelegated( event BorrowAllowanceDelegated(
address indexed asset,
address indexed fromUser, address indexed fromUser,
address indexed toUser, address indexed toUser,
uint256 interestRateMode, address[] assets,
uint256 amount uint256[] interestRateModes,
uint256[] amounts
); );
/** /**
* @dev emitted on borrow * @dev emitted on borrow
@ -196,17 +196,17 @@ interface ILendingPool {
) external; ) external;
/** /**
* @dev Sets allowance to borrow on a certain type of debt asset for a certain user address * @dev Sets allowance to borrow on a certain type of debt assets for a certain user address
* @param asset The underlying asset of the debt token * @param assets The underlying asset of each debt token
* @param user The user to give allowance to * @param user The user to give allowance to
* @param interestRateMode Type of debt: 1 for stable, 2 for variable * @param interestRateModes Types of debt: 1 for stable, 2 for variable
* @param amount Allowance amount to borrow * @param amounts Allowance amounts to borrow
**/ **/
function delegateBorrowAllowance( function delegateBorrowAllowance(
address asset, address[] calldata assets,
address user, address user,
uint256 interestRateMode, uint256[] calldata interestRateModes,
uint256 amount uint256[] calldata amounts
) external; ) external;
function getBorrowAllowance( 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 * @dev Sets allowance to borrow on a certain type of debt assets for a certain user address
* @param asset The underlying asset of the debt token * @param assets The underlying asset of each debt token
* @param user The user to give allowance to * @param user The user to give allowance to
* @param interestRateMode Type of debt: 1 for stable, 2 for variable * @param interestRateModes Types of debt: 1 for stable, 2 for variable
* @param amount Allowance amount to borrow * @param amounts Allowance amounts to borrow
**/ **/
function delegateBorrowAllowance( function delegateBorrowAllowance(
address asset, address[] calldata assets,
address user, address user,
uint256 interestRateMode, uint256[] calldata interestRateModes,
uint256 amount uint256[] calldata amounts
) external override { ) external override {
_whenNotPaused(); _whenNotPaused();
address debtToken = _reserves[asset].getDebtTokenAddress(interestRateMode);
_borrowAllowance[debtToken][msg.sender][user] = amount; uint256 countAssets = assets.length;
emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount); 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 * @param asset the address of the reserve
* @return the reserve normalized income * @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(); return _reserves[asset].getNormalizedIncome();
} }

View File

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

View File

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

View File

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

View File

@ -1,7 +1,9 @@
pragma solidity ^0.6.8; pragma solidity ^0.6.8;
pragma experimental ABIEncoderV2; 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 {UserConfiguration} from '../../contracts/libraries/configuration/UserConfiguration.sol';
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';
@ -12,7 +14,6 @@ Certora: Harness that delegates calls to the original LendingPool.
Used for the verification of the VariableDebtToken contract. Used for the verification of the VariableDebtToken contract.
*/ */
contract LendingPoolHarnessForVariableDebtToken is ILendingPool { contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
LendingPool private originalPool; LendingPool private originalPool;
function deposit( function deposit(
@ -91,26 +92,36 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
return originalPool.getReservesList(); 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); 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); return originalPool.getUserConfiguration(user);
} }
function getUserAccountData(address user) function getUserAccountData(address user)
external external
override override
view view
returns ( returns (
uint256 totalCollateralETH, uint256 totalCollateralETH,
uint256 totalBorrowsETH, uint256 totalBorrowsETH,
uint256 availableBorrowsETH, uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold, uint256 currentLiquidationThreshold,
uint256 ltv, uint256 ltv,
uint256 healthFactor uint256 healthFactor
) )
{ {
return originalPool.getUserAccountData(user); return originalPool.getUserAccountData(user);
} }
@ -122,12 +133,18 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
address variableDebtAddress, address variableDebtAddress,
address interestRateStrategyAddress address interestRateStrategyAddress
) external override { ) external override {
originalPool.initReserve(asset, aTokenAddress, stableDebtAddress, variableDebtAddress, interestRateStrategyAddress); originalPool.initReserve(
asset,
aTokenAddress,
stableDebtAddress,
variableDebtAddress,
interestRateStrategyAddress
);
} }
function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
external external
override override
{ {
originalPool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress); originalPool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress);
} }
@ -137,10 +154,10 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
} }
function getConfiguration(address asset) function getConfiguration(address asset)
external external
override override
view view
returns (ReserveConfiguration.Map memory) returns (ReserveConfiguration.Map memory)
{ {
return originalPool.getConfiguration(asset); return originalPool.getConfiguration(asset);
} }
@ -155,10 +172,10 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
mapping(uint256 => uint256) private reserveNormalizedVariableDebt; mapping(uint256 => uint256) private reserveNormalizedVariableDebt;
function getReserveNormalizedVariableDebt(address asset) function getReserveNormalizedVariableDebt(address asset)
external external
override override
view view
returns (uint256) returns (uint256)
{ {
require(reserveNormalizedVariableDebt[block.timestamp] == 1e27); require(reserveNormalizedVariableDebt[block.timestamp] == 1e27);
return reserveNormalizedVariableDebt[block.timestamp]; return reserveNormalizedVariableDebt[block.timestamp];
@ -194,5 +211,4 @@ contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
) external override { ) external override {
originalPool.finalizeTransfer(asset, from, to, amount, balanceFromAfter, balanceToBefore); 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'; import {IncentivizedERC20} from '../../contracts/tokenization/IncentivizedERC20.sol';
contract StableDebtTokenHarness is StableDebtToken { 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( function balanceOf(address account) public override view returns (uint256) {
address pool, return IncentivizedERC20.balanceOf(account);
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) { function _calcTotalSupply(uint256 avgRate) internal override view returns (uint256) {
return IncentivizedERC20.balanceOf(account); return IncentivizedERC20.totalSupply();
} }
function _calcTotalSupply(uint256 avgRate) internal override view returns (uint256) { function getIncentivesController() public view returns (address) {
return IncentivizedERC20.totalSupply(); 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. A wrapper contract for calling functions from the library UserConfiguration.
*/ */
contract UserConfigurationHarness { 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( function setUsingAsCollateral(
address user, address user,
uint256 reserveIndex, uint256 reserveIndex,
bool borrowing bool _usingAsCollateral
) public { ) public {
UserConfiguration.setBorrowing(usersConfig, reserveIndex, borrowing); UserConfiguration.setUsingAsCollateral(usersConfig, reserveIndex, _usingAsCollateral);
} }
function setUsingAsCollateral( function isUsingAsCollateralOrBorrowing(address user, uint256 reserveIndex)
address user, public
uint256 reserveIndex,
bool _usingAsCollateral
) public {
UserConfiguration.setUsingAsCollateral(usersConfig, reserveIndex, _usingAsCollateral);
}
function isUsingAsCollateralOrBorrowing(
address user,
uint256 reserveIndex)
public
view view
returns (bool) { returns (bool)
return UserConfiguration.isUsingAsCollateralOrBorrowing(usersConfig, reserveIndex); {
} return UserConfiguration.isUsingAsCollateralOrBorrowing(usersConfig, reserveIndex);
}
function isBorrowing( function isBorrowing(address user, uint256 reserveIndex) public view returns (bool) {
address user, return UserConfiguration.isBorrowing(usersConfig, reserveIndex);
uint256 reserveIndex) }
public
view
returns (bool) {
return UserConfiguration.isBorrowing(usersConfig, reserveIndex);
}
function isUsingAsCollateral( function isUsingAsCollateral(address user, uint256 reserveIndex) public view returns (bool) {
address user, return UserConfiguration.isUsingAsCollateral(usersConfig, reserveIndex);
uint256 reserveIndex) }
public
view
returns (bool) {
return UserConfiguration.isUsingAsCollateral(usersConfig, reserveIndex);
}
function isBorrowingAny( function isBorrowingAny(address user) public view returns (bool) {
address user) return UserConfiguration.isBorrowingAny(usersConfig);
public }
view
returns (bool) {
return UserConfiguration.isBorrowingAny(usersConfig);
}
function isEmpty( function isEmpty(address user) public view returns (bool) {
address user) return UserConfiguration.isEmpty(usersConfig);
public }
view
returns (bool) { /*
return UserConfiguration.isEmpty(usersConfig);
}
/*
Mimics the original constructor of the contract. 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 // Deposited for onBehalfOf user already, delegate borrow allowance
await pool await pool
.connect(onBehalfOf.signer) .connect(onBehalfOf.signer)
.delegateBorrowAllowance(weth.address, caller.address, 1, flashAmount); .delegateBorrowAllowance([weth.address], caller.address, [1], [flashAmount]);
await _mockFlashLoanReceiver.setFailExecutionTransfer(true); await _mockFlashLoanReceiver.setFailExecutionTransfer(true);

View File

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

View File

@ -133,7 +133,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
// Try to execute liquidation // Try to execute liquidation
await expect( 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); ).revertedWith(IS_PAUSED);
// Unpause the pool // Unpause the pool