From 3173bee782247febe77b3d778a86b70748d9e488 Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 9 Sep 2020 15:33:05 +0300 Subject: [PATCH 1/9] initial implementation of the credit delegation + basic tests --- contracts/interfaces/ILendingPool.sol | 24 +++++- contracts/lendingpool/LendingPool.sol | 64 ++++++++++----- contracts/libraries/helpers/Errors.sol | 5 +- contracts/libraries/logic/ValidationLogic.sol | 14 ++-- deployed-contracts.json | 8 +- test/atoken-transfer.spec.ts | 16 +++- test/helpers/actions.ts | 45 +++++++++-- test/helpers/scenario-engine.ts | 31 +++++++- .../scenarios/borrow-repay-variable.json | 79 +++++++++++++++++++ test/helpers/utils/helpers.ts | 5 +- test/liquidation-atoken.spec.ts | 4 +- test/liquidation-underlying.spec.ts | 4 +- 12 files changed, 250 insertions(+), 49 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index a7a5e1ca..bfeffc74 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -28,6 +28,12 @@ interface ILendingPool { **/ event Withdraw(address indexed reserve, address indexed user, uint256 amount); + event BorrowAllowanceDelegated( + address indexed fromUser, + address indexed toUser, + address indexed asset, + uint256 amount + ); /** * @dev emitted on borrow * @param reserve the address of the reserve @@ -39,7 +45,8 @@ interface ILendingPool { **/ event Borrow( address indexed reserve, - address indexed user, + address user, + address indexed onBehalfOf, uint256 amount, uint256 borrowRateMode, uint256 borrowRate, @@ -149,6 +156,18 @@ interface ILendingPool { **/ function withdraw(address reserve, uint256 amount) external; + function delegateBorrowAllowance( + address user, + address asset, + uint256 amount + ) external; + + function getBorrowAllowance( + address fromUser, + address toUser, + address asset + ) external view returns (uint256); + /** * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower * already deposited enough collateral. @@ -160,7 +179,8 @@ interface ILendingPool { address reserve, uint256 amount, uint256 interestRateMode, - uint16 referralCode + uint16 referralCode, + address onBehalfOf ) external; /** diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 9e4e6707..5f2f8158 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -48,7 +48,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { mapping(address => ReserveLogic.ReserveData) internal _reserves; mapping(address => UserConfiguration.Map) internal _usersConfig; - + mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance; address[] internal _reservesList; /** @@ -157,6 +157,23 @@ contract LendingPool is VersionedInitializable, ILendingPool { emit Withdraw(asset, msg.sender, amount); } + function getBorrowAllowance( + address fromUser, + address toUser, + address asset + ) external override view returns (uint256) { + return _borrowAllowance[fromUser][asset][toUser]; + } + + function delegateBorrowAllowance( + address user, + address asset, + uint256 amount + ) external override { + _borrowAllowance[msg.sender][asset][user] = amount; + emit BorrowAllowanceDelegated(msg.sender, user, asset, amount); + } + /** * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower * already deposited enough collateral. @@ -169,12 +186,19 @@ contract LendingPool is VersionedInitializable, ILendingPool { address asset, uint256 amount, uint256 interestRateMode, - uint16 referralCode + uint16 referralCode, + address onBehalfOf ) external override { + if (onBehalfOf != msg.sender) { + _borrowAllowance[onBehalfOf][asset][msg.sender] = _borrowAllowance[onBehalfOf][asset][msg + .sender] + .sub(amount, Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH); + } _executeBorrow( ExecuteBorrowParams( asset, msg.sender, + onBehalfOf, amount, interestRateMode, _reserves[asset].aTokenAddress, @@ -450,21 +474,20 @@ contract LendingPool is VersionedInitializable, ILendingPool { vars.amountPlusPremium = amount.add(vars.premium); if (debtMode == ReserveLogic.InterestRateMode.NONE) { - IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium); - + reserve.updateCumulativeIndexesAndTimestamp(); reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium); reserve.updateInterestRates(asset, vars.aTokenAddress, vars.premium, 0); - - emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode); + emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode); } else { // If the transfer didn't succeed, the receiver either didn't return the funds, or didn't approve the transfer. _executeBorrow( ExecuteBorrowParams( asset, msg.sender, + msg.sender, vars.amountPlusPremium.sub(vars.availableBalance), mode, vars.aTokenAddress, @@ -694,6 +717,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { struct ExecuteBorrowParams { address asset; address user; + address onBehalfOf; uint256 amount; uint256 interestRateMode; address aTokenAddress; @@ -707,7 +731,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { **/ function _executeBorrow(ExecuteBorrowParams memory vars) internal { 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(); @@ -717,6 +741,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { ValidationLogic.validateBorrow( reserve, + vars.onBehalfOf, vars.asset, vars.amount, amountInETH, @@ -728,13 +753,11 @@ contract LendingPool is VersionedInitializable, ILendingPool { oracle ); - uint256 reserveIndex = reserve.index; if (!userConfig.isBorrowing(reserveIndex)) { userConfig.setBorrowing(reserveIndex, true); } - reserve.updateCumulativeIndexesAndTimestamp(); //caching the current stable borrow rate @@ -746,24 +769,29 @@ contract LendingPool is VersionedInitializable, ILendingPool { currentStableRate = reserve.currentStableBorrowRate; IStableDebtToken(reserve.stableDebtTokenAddress).mint( - vars.user, + vars.onBehalfOf, vars.amount, currentStableRate ); } else { - IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.user, vars.amount); + IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.onBehalfOf, vars.amount); } - reserve.updateInterestRates(vars.asset, vars.aTokenAddress, 0, vars.releaseUnderlying ? vars.amount : 0); - - if(vars.releaseUnderlying){ - IAToken(vars.aTokenAddress).transferUnderlyingTo(msg.sender, vars.amount); + reserve.updateInterestRates( + vars.asset, + vars.aTokenAddress, + 0, + vars.releaseUnderlying ? vars.amount : 0 + ); + + if (vars.releaseUnderlying) { + IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); } - - + emit Borrow( vars.asset, - msg.sender, + vars.user, + vars.onBehalfOf, vars.amount, vars.interestRateMode, ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE diff --git a/contracts/libraries/helpers/Errors.sol b/contracts/libraries/helpers/Errors.sol index 974aa5e1..ba3ee2b9 100644 --- a/contracts/libraries/helpers/Errors.sol +++ b/contracts/libraries/helpers/Errors.sol @@ -38,6 +38,7 @@ library Errors { 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 INVALID_FLASHLOAN_MODE = '43'; //Invalid flashloan mode selected + string public constant BORROW_ALLOWANCE_ARE_NOT_ENOUGH = '52'; // User borrows on behalf, but allowance are too small // require error messages - aToken string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool' @@ -70,7 +71,7 @@ library Errors { string public constant NO_ERRORS = '42'; // 'No errors' //require error messages - Math libraries - string public constant MULTIPLICATION_OVERFLOW = '44'; - string public constant ADDITION_OVERFLOW = '45'; + string public constant MULTIPLICATION_OVERFLOW = '44'; + string public constant ADDITION_OVERFLOW = '45'; string public constant DIVISION_BY_ZERO = '46'; } diff --git a/contracts/libraries/logic/ValidationLogic.sol b/contracts/libraries/logic/ValidationLogic.sol index 7a640458..69c4c4b6 100644 --- a/contracts/libraries/logic/ValidationLogic.sol +++ b/contracts/libraries/logic/ValidationLogic.sol @@ -100,6 +100,7 @@ library ValidationLogic { /** * @dev validates a borrow. * @param reserve the reserve state from which the user is borrowing + * @param userAddress the address of the user * @param reserveAddress the address of the reserve * @param amount the amount to be borrowed * @param amountInETH the amount to be borrowed, in ETH @@ -113,6 +114,7 @@ library ValidationLogic { function validateBorrow( ReserveLogic.ReserveData storage reserve, + address userAddress, address reserveAddress, uint256 amount, uint256 amountInETH, @@ -151,7 +153,7 @@ library ValidationLogic { vars.currentLiquidationThreshold, vars.healthFactor ) = GenericLogic.calculateUserAccountData( - msg.sender, + userAddress, reservesData, userConfig, reserves, @@ -192,7 +194,7 @@ library ValidationLogic { require( !userConfig.isUsingAsCollateral(reserve.index) || reserve.configuration.getLtv() == 0 || - amount > IERC20(reserve.aTokenAddress).balanceOf(msg.sender), + amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress), Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY ); @@ -321,10 +323,10 @@ library ValidationLogic { } /** - * @dev validates a flashloan action - * @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan) - * @param premium the premium paid on the flashloan - **/ + * @dev validates a flashloan action + * @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan) + * @param premium the premium paid on the flashloan + **/ function validateFlashloan(uint256 mode, uint256 premium) internal pure { require(premium > 0, Errors.REQUESTED_AMOUNT_TOO_SMALL); require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE); diff --git a/deployed-contracts.json b/deployed-contracts.json index d04816b9..56111eb8 100644 --- a/deployed-contracts.json +++ b/deployed-contracts.json @@ -111,7 +111,7 @@ }, "DefaultReserveInterestRateStrategy": { "buidlerevm": { - "address": "0x626FdE749F9d499d3777320CAf29484B624ab84a", + "address": "0x2530ce07D254eA185E8e0bCC37a39e2FbA3bE548", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -422,7 +422,7 @@ }, "StableDebtToken": { "buidlerevm": { - "address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6", + "address": "0x0Cf45557d25a4e4c0F1aC65EF6c48ae67c61a0E6", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -432,7 +432,7 @@ }, "VariableDebtToken": { "buidlerevm": { - "address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d", + "address": "0x7fAeC7791277Ff512c41CA903c177B2Ed952dDAc", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -446,7 +446,7 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "buidlerevm": { - "address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E", + "address": "0x33958cC3535Fc328369EAC2B2Bebd120D67C7fa1", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, diff --git a/test/atoken-transfer.spec.ts b/test/atoken-transfer.spec.ts index 1c161608..c986fb9b 100644 --- a/test/atoken-transfer.spec.ts +++ b/test/atoken-transfer.spec.ts @@ -103,7 +103,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { await expect( pool .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 ).to.be.revertedWith(COLLATERAL_BALANCE_IS_0); }); @@ -116,7 +122,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => { await pool .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( aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer), diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index be0daac5..c8e93d2d 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -278,11 +278,34 @@ export const withdraw = async ( } }; +export const delegateBorrowAllowance = async ( + reserveSymbol: string, + amount: string, + user: SignerWithAddress, + receiver: tEthereumAddress, + testEnv: TestEnv +) => { + const {pool} = testEnv; + + const reserve = await getReserveAddressFromSymbol(reserveSymbol); + const amountToDelegate = await convertToCurrencyDecimals(reserve, amount); + + await pool + .connect(user.signer) + .delegateBorrowAllowance(receiver, reserve, amountToDelegate.toString()); + expect( + ( + await pool['getBorrowAllowance(address,address,address)'](user.address, receiver, reserve) + ).toString() + ).to.be.equal(amountToDelegate.toString(), 'borrowAllowance are set incorrectly'); +}; + export const borrow = async ( reserveSymbol: string, amount: string, interestRateMode: string, user: SignerWithAddress, + onBehalfOf: tEthereumAddress, timeTravel: string, expectedResult: string, testEnv: TestEnv, @@ -294,15 +317,18 @@ export const borrow = async ( const {reserveData: reserveDataBefore, userData: userDataBefore} = await getContractsData( reserve, - user.address, - testEnv + onBehalfOf, + testEnv, + user.address ); const amountToBorrow = await convertToCurrencyDecimals(reserve, amount); if (expectedResult === 'success') { 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); @@ -317,7 +343,7 @@ export const borrow = async ( reserveData: reserveDataAfter, userData: userDataAfter, timestamp, - } = await getContractsData(reserve, user.address, testEnv); + } = await getContractsData(reserve, onBehalfOf, testEnv, user.address); const expectedReserveData = calcExpectedReserveDataAfterBorrow( amountToBorrow.toString(), @@ -364,7 +390,7 @@ export const borrow = async ( // }); } else if (expectedResult === 'revert') { await expect( - pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0'), + pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf), revertMessage ).to.be.reverted; } @@ -845,10 +871,15 @@ const getTxCostAndTimestamp = async (tx: ContractReceipt) => { return {txCost, txTimestamp}; }; -const getContractsData = async (reserve: string, user: string, testEnv: TestEnv) => { +const getContractsData = async ( + reserve: string, + user: string, + testEnv: TestEnv, + sender?: string +) => { const {pool} = testEnv; const reserveData = await getReserveData(pool, reserve); - const userData = await getUserData(pool, reserve, user); + const userData = await getUserData(pool, reserve, user, sender || user); const timestamp = await timeLatest(); return { diff --git a/test/helpers/scenario-engine.ts b/test/helpers/scenario-engine.ts index 735d3b84..8092d671 100644 --- a/test/helpers/scenario-engine.ts +++ b/test/helpers/scenario-engine.ts @@ -12,6 +12,7 @@ import { redirectInterestStream, redirectInterestStreamOf, allowInterestRedirectionTo, + delegateBorrowAllowance, } from './actions'; import {RateMode} from '../../helpers/types'; @@ -102,6 +103,18 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv } 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, user, toUser, testEnv); + } + break; + case 'withdraw': { const {amount} = action.args; @@ -115,13 +128,27 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv break; 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 === '') { 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; diff --git a/test/helpers/scenarios/borrow-repay-variable.json b/test/helpers/scenarios/borrow-repay-variable.json index 46c17610..6440c822 100644 --- a/test/helpers/scenarios/borrow-repay-variable.json +++ b/test/helpers/scenarios/borrow-repay-variable.json @@ -955,6 +955,85 @@ "expected": "success" } ] + }, + { + "description": "User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 1 WETH on behalf of user 0", + "actions": [ + { + "name": "mint", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "approve", + "args": { + "reserve": "DAI", + "user": "0" + }, + "expected": "success" + }, + { + "name": "deposit", + "args": { + "reserve": "DAI", + "amount": "1000", + "user": "0" + }, + "expected": "success" + }, + { + "name": "delegateBorrowAllowance", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0", + "toUser": "4" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "4", + "onBehalfOf": "0", + "borrowRateMode": "variable" + }, + "expected": "success" + } + ] + }, + { + "description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 2 WETH on behalf of user 0, revert expected", + "actions": [ + { + "name": "delegateBorrowAllowance", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0", + "toUser": "4" + }, + "expected": "success" + }, + { + "name": "borrow", + "args": { + "reserve": "WETH", + "amount": "2", + "user": "4", + "onBehalfOf": "0", + "borrowRateMode": "variable" + }, + "expected": "revert", + "revertMessage": "52" + } + ] } ] } diff --git a/test/helpers/utils/helpers.ts b/test/helpers/utils/helpers.ts index c20c67ac..9dac685b 100644 --- a/test/helpers/utils/helpers.ts +++ b/test/helpers/utils/helpers.ts @@ -61,7 +61,8 @@ export const getReserveData = async ( export const getUserData = async ( pool: LendingPool, reserve: string, - user: string + user: tEthereumAddress, + sender?: tEthereumAddress ): Promise => { const [userData, aTokenData] = await Promise.all([ pool.getUserReserveData(reserve, user), @@ -77,7 +78,7 @@ export const getUserData = async ( ] = aTokenData; const token = await getMintableErc20(reserve); - const walletBalance = new BigNumber((await token.balanceOf(user)).toString()); + const walletBalance = new BigNumber((await token.balanceOf(sender || user)).toString()); return { principalATokenBalance: new BigNumber(principalATokenBalance), diff --git a/test/liquidation-atoken.spec.ts b/test/liquidation-atoken.spec.ts index 921114f0..9c01b735 100644 --- a/test/liquidation-atoken.spec.ts +++ b/test/liquidation-atoken.spec.ts @@ -59,7 +59,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => await pool .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); @@ -252,7 +252,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) => await pool .connect(borrower.signer) - .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0'); + .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address); //drops HF below 1 diff --git a/test/liquidation-underlying.spec.ts b/test/liquidation-underlying.spec.ts index 064e3856..5c46e403 100644 --- a/test/liquidation-underlying.spec.ts +++ b/test/liquidation-underlying.spec.ts @@ -56,7 +56,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', await pool .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); @@ -222,7 +222,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset', await pool .connect(borrower.signer) - .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0'); + .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address); //drops HF below 1 await oracle.setAssetPrice( From 4058a7284c7ca75ce41e8b20365618562dfb2aed Mon Sep 17 00:00:00 2001 From: David Racero Date: Fri, 11 Sep 2020 12:59:19 +0200 Subject: [PATCH 2/9] Update storage layout of LendingPoolLiquidationManager to add new mapping from LendingPool. Fixes delegatecall liquidation error. --- contracts/lendingpool/LendingPool.sol | 1 + contracts/lendingpool/LendingPoolLiquidationManager.sol | 1 + 2 files changed, 2 insertions(+) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 11121f19..01b31dab 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -49,6 +49,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { mapping(address => ReserveLogic.ReserveData) internal _reserves; mapping(address => UserConfiguration.Map) internal _usersConfig; mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance; + address[] internal _reservesList; bool internal _flashLiquidationLocked; diff --git a/contracts/lendingpool/LendingPoolLiquidationManager.sol b/contracts/lendingpool/LendingPoolLiquidationManager.sol index 6c7b340b..ddd29256 100644 --- a/contracts/lendingpool/LendingPoolLiquidationManager.sol +++ b/contracts/lendingpool/LendingPoolLiquidationManager.sol @@ -43,6 +43,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable { mapping(address => ReserveLogic.ReserveData) internal reserves; mapping(address => UserConfiguration.Map) internal usersConfig; + mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance; address[] internal reservesList; From 1fbe84df492ee61d621d68bafcb0a8b9e9c83405 Mon Sep 17 00:00:00 2001 From: David Racero Date: Mon, 14 Sep 2020 10:46:57 +0200 Subject: [PATCH 3/9] Remove unused parameter to allow compilation without optimization for coverage. --- contracts/lendingpool/LendingPool.sol | 1 - contracts/libraries/logic/ValidationLogic.sol | 2 -- 2 files changed, 3 deletions(-) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 01b31dab..207c0d4a 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -803,7 +803,6 @@ contract LendingPool is VersionedInitializable, ILendingPool { ValidationLogic.validateBorrow( reserve, vars.onBehalfOf, - vars.asset, vars.amount, amountInETH, vars.interestRateMode, diff --git a/contracts/libraries/logic/ValidationLogic.sol b/contracts/libraries/logic/ValidationLogic.sol index 69c4c4b6..2bd29503 100644 --- a/contracts/libraries/logic/ValidationLogic.sol +++ b/contracts/libraries/logic/ValidationLogic.sol @@ -101,7 +101,6 @@ library ValidationLogic { * @dev validates a borrow. * @param reserve the reserve state from which the user is borrowing * @param userAddress the address of the user - * @param reserveAddress the address of the reserve * @param amount the amount to be borrowed * @param amountInETH the amount to be borrowed, in ETH * @param interestRateMode the interest rate mode at which the user is borrowing @@ -115,7 +114,6 @@ library ValidationLogic { function validateBorrow( ReserveLogic.ReserveData storage reserve, address userAddress, - address reserveAddress, uint256 amount, uint256 amountInETH, uint256 interestRateMode, From c0c31337634b7362c34acb363b9c8aeede54e370 Mon Sep 17 00:00:00 2001 From: andyk Date: Mon, 14 Sep 2020 13:52:05 +0300 Subject: [PATCH 4/9] makecredit delegation per debt token --- contracts/interfaces/ILendingPool.sol | 7 +- contracts/lendingpool/LendingPool.sol | 36 +++-- deployed-contracts.json | 16 +-- test/helpers/actions.ts | 7 +- test/helpers/scenario-engine.ts | 4 +- test/helpers/scenarios/borrow-delegation.json | 130 ++++++++++++++++++ .../scenarios/borrow-repay-variable.json | 79 ----------- 7 files changed, 176 insertions(+), 103 deletions(-) create mode 100644 test/helpers/scenarios/borrow-delegation.json diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index f5074c4d..177b39df 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -29,9 +29,10 @@ interface ILendingPool { event Withdraw(address indexed reserve, address indexed user, uint256 amount); event BorrowAllowanceDelegated( + address indexed asset, address indexed fromUser, address indexed toUser, - address indexed asset, + uint256 interestRateMode, uint256 amount ); /** @@ -159,13 +160,15 @@ interface ILendingPool { function delegateBorrowAllowance( address user, address asset, + uint256 interestRateMode, uint256 amount ) external; function getBorrowAllowance( address fromUser, address toUser, - address asset + address asset, + uint256 interestRateMode ) external view returns (uint256); /** diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 207c0d4a..48f4b7fe 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -48,6 +48,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { mapping(address => ReserveLogic.ReserveData) internal _reserves; 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; @@ -163,18 +164,28 @@ contract LendingPool is VersionedInitializable, ILendingPool { function getBorrowAllowance( address fromUser, address toUser, - address asset + address asset, + uint256 interestRateMode ) external override view returns (uint256) { - return _borrowAllowance[fromUser][asset][toUser]; + address debtToken = ReserveLogic.InterestRateMode.STABLE == + ReserveLogic.InterestRateMode(interestRateMode) + ? _reserves[asset].stableDebtTokenAddress + : _reserves[asset].variableDebtTokenAddress; + return _borrowAllowance[debtToken][fromUser][toUser]; } function delegateBorrowAllowance( address user, address asset, + uint256 interestRateMode, uint256 amount ) external override { - _borrowAllowance[msg.sender][asset][user] = amount; - emit BorrowAllowanceDelegated(msg.sender, user, asset, amount); + address debtToken = ReserveLogic.InterestRateMode.STABLE == + ReserveLogic.InterestRateMode(interestRateMode) + ? _reserves[asset].stableDebtTokenAddress + : _reserves[asset].variableDebtTokenAddress; + _borrowAllowance[debtToken][msg.sender][user] = amount; + emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount); } /** @@ -192,10 +203,19 @@ contract LendingPool is VersionedInitializable, ILendingPool { uint16 referralCode, address onBehalfOf ) external override { + ReserveLogic.ReserveData storage reserve = _reserves[asset]; + if (onBehalfOf != msg.sender) { - _borrowAllowance[onBehalfOf][asset][msg.sender] = _borrowAllowance[onBehalfOf][asset][msg - .sender] - .sub(amount, Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH); + address debtToken = ReserveLogic.InterestRateMode.STABLE == + ReserveLogic.InterestRateMode(interestRateMode) + ? reserve.stableDebtTokenAddress + : reserve.variableDebtTokenAddress; + + _borrowAllowance[debtToken][onBehalfOf][msg + .sender] = _borrowAllowance[debtToken][onBehalfOf][msg.sender].sub( + amount, + Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH + ); } _executeBorrow( ExecuteBorrowParams( @@ -204,7 +224,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { onBehalfOf, amount, interestRateMode, - _reserves[asset].aTokenAddress, + reserve.aTokenAddress, referralCode, true ) diff --git a/deployed-contracts.json b/deployed-contracts.json index b7a09a38..5abe0277 100644 --- a/deployed-contracts.json +++ b/deployed-contracts.json @@ -111,7 +111,7 @@ }, "DefaultReserveInterestRateStrategy": { "buidlerevm": { - "address": "0x2530ce07D254eA185E8e0bCC37a39e2FbA3bE548", + "address": "0x626FdE749F9d499d3777320CAf29484B624ab84a", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -422,7 +422,7 @@ }, "StableDebtToken": { "buidlerevm": { - "address": "0x0Cf45557d25a4e4c0F1aC65EF6c48ae67c61a0E6", + "address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -432,7 +432,7 @@ }, "VariableDebtToken": { "buidlerevm": { - "address": "0x7fAeC7791277Ff512c41CA903c177B2Ed952dDAc", + "address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -446,13 +446,13 @@ "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "buidlerevm": { - "address": "0x33958cC3535Fc328369EAC2B2Bebd120D67C7fa1", + "address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" } }, "MockAToken": { "buidlerevm": { - "address": "0x3bDA11B584dDff7F66E0cFe1da1562c92B45db60", + "address": "0x392E5355a0e88Bd394F717227c752670fb3a8020", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -472,7 +472,7 @@ }, "MockStableDebtToken": { "buidlerevm": { - "address": "0x392E5355a0e88Bd394F717227c752670fb3a8020", + "address": "0x3b050AFb4ac4ACE646b31fF3639C1CD43aC31460", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -482,7 +482,7 @@ }, "MockVariableDebtToken": { "buidlerevm": { - "address": "0x3b050AFb4ac4ACE646b31fF3639C1CD43aC31460", + "address": "0xEBAB67ee3ef604D5c250A53b4b8fcbBC6ec3007C", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -495,4 +495,4 @@ "address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2" } } -} +} \ No newline at end of file diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index d8d29d72..a2bef096 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -281,6 +281,7 @@ export const withdraw = async ( export const delegateBorrowAllowance = async ( reserveSymbol: string, amount: string, + interestRateMode: string, user: SignerWithAddress, receiver: tEthereumAddress, testEnv: TestEnv @@ -292,11 +293,9 @@ export const delegateBorrowAllowance = async ( await pool .connect(user.signer) - .delegateBorrowAllowance(receiver, reserve, amountToDelegate.toString()); + .delegateBorrowAllowance(receiver, reserve, interestRateMode, amountToDelegate.toString()); expect( - ( - await pool['getBorrowAllowance(address,address,address)'](user.address, receiver, reserve) - ).toString() + (await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateMode)).toString() ).to.be.equal(amountToDelegate.toString(), 'borrowAllowance are set incorrectly'); }; diff --git a/test/helpers/scenario-engine.ts b/test/helpers/scenario-engine.ts index 8092d671..8de5780d 100644 --- a/test/helpers/scenario-engine.ts +++ b/test/helpers/scenario-engine.ts @@ -63,7 +63,7 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv if (borrowRateMode) { if (borrowRateMode === 'none') { - RateMode.None; + rateMode = RateMode.None; } else if (borrowRateMode === 'stable') { rateMode = RateMode.Stable; } else if (borrowRateMode === 'variable') { @@ -111,7 +111,7 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv throw `Invalid amount to deposit into the ${reserve} reserve`; } - await delegateBorrowAllowance(reserve, amount, user, toUser, testEnv); + await delegateBorrowAllowance(reserve, amount, rateMode, user, toUser, testEnv); } break; diff --git a/test/helpers/scenarios/borrow-delegation.json b/test/helpers/scenarios/borrow-delegation.json new file mode 100644 index 00000000..eed21c5a --- /dev/null +++ b/test/helpers/scenarios/borrow-delegation.json @@ -0,0 +1,130 @@ +{ + "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 on behalf of user 0, revert expected", + "actions": [ + { + "name": "delegateBorrowAllowance", + "args": { + "reserve": "WETH", + "amount": "1", + "user": "0", + "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" + } + ] + } + ] +} diff --git a/test/helpers/scenarios/borrow-repay-variable.json b/test/helpers/scenarios/borrow-repay-variable.json index befae019..46c17610 100644 --- a/test/helpers/scenarios/borrow-repay-variable.json +++ b/test/helpers/scenarios/borrow-repay-variable.json @@ -955,85 +955,6 @@ "expected": "success" } ] - }, - { - "description": "User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 1 WETH on behalf of user 0", - "actions": [ - { - "name": "mint", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "approve", - "args": { - "reserve": "DAI", - "user": "0" - }, - "expected": "success" - }, - { - "name": "deposit", - "args": { - "reserve": "DAI", - "amount": "1000", - "user": "0" - }, - "expected": "success" - }, - { - "name": "delegateBorrowAllowance", - "args": { - "reserve": "WETH", - "amount": "1", - "user": "0", - "toUser": "4" - }, - "expected": "success" - }, - { - "name": "borrow", - "args": { - "reserve": "WETH", - "amount": "1", - "user": "4", - "onBehalfOf": "0", - "borrowRateMode": "variable" - }, - "expected": "success" - } - ] - }, - { - "description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 2 WETH on behalf of user 0, revert expected", - "actions": [ - { - "name": "delegateBorrowAllowance", - "args": { - "reserve": "WETH", - "amount": "1", - "user": "0", - "toUser": "4" - }, - "expected": "success" - }, - { - "name": "borrow", - "args": { - "reserve": "WETH", - "amount": "2", - "user": "4", - "onBehalfOf": "0", - "borrowRateMode": "variable" - }, - "expected": "revert", - "revertMessage": "54" - } - ] } ] } From d135c25b39ae16ceb7fb7bd6762f11f9cfb5169c Mon Sep 17 00:00:00 2001 From: andyk Date: Mon, 14 Sep 2020 15:41:44 +0300 Subject: [PATCH 5/9] add borrowRateMode check on credit delegation --- contracts/lendingpool/LendingPool.sol | 18 +++++------------ contracts/libraries/logic/ReserveLogic.sol | 16 +++++++++++++++ test/helpers/actions.ts | 18 ++++++++++++----- test/helpers/scenario-engine.ts | 11 +++++++++- ...delegation.json => credit-delegation.json} | 20 ++++++++++++++++++- 5 files changed, 63 insertions(+), 20 deletions(-) rename test/helpers/scenarios/{borrow-delegation.json => credit-delegation.json} (84%) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 48f4b7fe..f0e3214e 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -167,11 +167,8 @@ contract LendingPool is VersionedInitializable, ILendingPool { address asset, uint256 interestRateMode ) external override view returns (uint256) { - address debtToken = ReserveLogic.InterestRateMode.STABLE == - ReserveLogic.InterestRateMode(interestRateMode) - ? _reserves[asset].stableDebtTokenAddress - : _reserves[asset].variableDebtTokenAddress; - return _borrowAllowance[debtToken][fromUser][toUser]; + return + _borrowAllowance[_reserves[asset].getDebtTokenAddress(interestRateMode)][fromUser][toUser]; } function delegateBorrowAllowance( @@ -180,10 +177,8 @@ contract LendingPool is VersionedInitializable, ILendingPool { uint256 interestRateMode, uint256 amount ) external override { - address debtToken = ReserveLogic.InterestRateMode.STABLE == - ReserveLogic.InterestRateMode(interestRateMode) - ? _reserves[asset].stableDebtTokenAddress - : _reserves[asset].variableDebtTokenAddress; + address debtToken = _reserves[asset].getDebtTokenAddress(interestRateMode); + _borrowAllowance[debtToken][msg.sender][user] = amount; emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount); } @@ -206,10 +201,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { ReserveLogic.ReserveData storage reserve = _reserves[asset]; if (onBehalfOf != msg.sender) { - address debtToken = ReserveLogic.InterestRateMode.STABLE == - ReserveLogic.InterestRateMode(interestRateMode) - ? reserve.stableDebtTokenAddress - : reserve.variableDebtTokenAddress; + address debtToken = reserve.getDebtTokenAddress(interestRateMode); _borrowAllowance[debtToken][onBehalfOf][msg .sender] = _borrowAllowance[debtToken][onBehalfOf][msg.sender].sub( diff --git a/contracts/libraries/logic/ReserveLogic.sol b/contracts/libraries/logic/ReserveLogic.sol index 06d08499..2f8b668a 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -116,6 +116,22 @@ library ReserveLogic { return cumulated; } + 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 * a formal specification. diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index a2bef096..c988c106 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -284,19 +284,27 @@ export const delegateBorrowAllowance = async ( interestRateMode: string, user: SignerWithAddress, receiver: tEthereumAddress, - testEnv: TestEnv + expectedResult: string, + testEnv: TestEnv, + revertMessage?: string ) => { const {pool} = testEnv; const reserve = await getReserveAddressFromSymbol(reserveSymbol); const amountToDelegate = await convertToCurrencyDecimals(reserve, amount); - await pool + const delegateAllowancePromise = pool .connect(user.signer) .delegateBorrowAllowance(receiver, reserve, interestRateMode, amountToDelegate.toString()); - expect( - (await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateMode)).toString() - ).to.be.equal(amountToDelegate.toString(), 'borrowAllowance are set incorrectly'); + 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 ( diff --git a/test/helpers/scenario-engine.ts b/test/helpers/scenario-engine.ts index 8de5780d..fd886114 100644 --- a/test/helpers/scenario-engine.ts +++ b/test/helpers/scenario-engine.ts @@ -111,7 +111,16 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv throw `Invalid amount to deposit into the ${reserve} reserve`; } - await delegateBorrowAllowance(reserve, amount, rateMode, user, toUser, testEnv); + await delegateBorrowAllowance( + reserve, + amount, + rateMode, + user, + toUser, + expected, + testEnv, + revertMessage + ); } break; diff --git a/test/helpers/scenarios/borrow-delegation.json b/test/helpers/scenarios/credit-delegation.json similarity index 84% rename from test/helpers/scenarios/borrow-delegation.json rename to test/helpers/scenarios/credit-delegation.json index eed21c5a..a67924ee 100644 --- a/test/helpers/scenarios/borrow-delegation.json +++ b/test/helpers/scenarios/credit-delegation.json @@ -73,7 +73,7 @@ ] }, { - "description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 3 WETH on behalf of user 0, revert expected", + "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", @@ -81,6 +81,7 @@ "reserve": "WETH", "amount": "1", "user": "0", + "borrowRateMode": "variable", "toUser": "4" }, "expected": "success" @@ -125,6 +126,23 @@ "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" + } + ] } ] } From 35bff4bea22fdb08866bc9c9067d8aaa6b3d5056 Mon Sep 17 00:00:00 2001 From: andyk Date: Mon, 14 Sep 2020 15:50:46 +0300 Subject: [PATCH 6/9] add comment for getDebtTokenAddress function --- contracts/libraries/logic/ReserveLogic.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/libraries/logic/ReserveLogic.sol b/contracts/libraries/logic/ReserveLogic.sol index 2f8b668a..1e5268ad 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -116,6 +116,12 @@ library ReserveLogic { return cumulated; } + /** + * @dev returns an address of the debt token used for particular interest rate mode on asset. + * @param reserve the reserve object + * @param interest rate mode - 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 From 7c67e0221c7c32ff213503b4b90ab72334685bb5 Mon Sep 17 00:00:00 2001 From: andyk Date: Mon, 14 Sep 2020 16:16:54 +0300 Subject: [PATCH 7/9] extra comment --- contracts/lendingpool/LendingPool.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index f0e3214e..f3fc796b 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -190,6 +190,7 @@ contract LendingPool is VersionedInitializable, ILendingPool { * @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 referralCode a referral code for integrators + * @param onBehalfOf address of the user who will receive the debt **/ function borrow( address asset, From 9d757aaa37ab21726bb073cfabe13f6ff1d429d1 Mon Sep 17 00:00:00 2001 From: andyk Date: Mon, 14 Sep 2020 17:57:59 +0300 Subject: [PATCH 8/9] fix docustring --- contracts/libraries/logic/ReserveLogic.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/libraries/logic/ReserveLogic.sol b/contracts/libraries/logic/ReserveLogic.sol index 1e5268ad..12e46b67 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -119,7 +119,7 @@ library ReserveLogic { /** * @dev returns an address of the debt token used for particular interest rate mode on asset. * @param reserve the reserve object - * @param interest rate mode - STABLE or VARIABLE from ReserveLogic.InterestRateMode enum + * @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) From 6d96be5f428aff3fe84276a96e5f0896eca4eeee Mon Sep 17 00:00:00 2001 From: eboado Date: Mon, 14 Sep 2020 17:18:57 +0200 Subject: [PATCH 9/9] - Added extra natspecs --- contracts/interfaces/ILendingPool.sol | 9 ++++++++- contracts/lendingpool/LendingPool.sol | 9 ++++++++- test/helpers/actions.ts | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index 177b39df..acd2b9a4 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -157,9 +157,16 @@ interface ILendingPool { **/ 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 user, address asset, + address user, uint256 interestRateMode, uint256 amount ) external; diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index f3fc796b..c494110b 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -171,9 +171,16 @@ contract LendingPool is VersionedInitializable, ILendingPool { _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 user, address asset, + address user, uint256 interestRateMode, uint256 amount ) external override { diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index c988c106..8c63b05f 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -295,7 +295,7 @@ export const delegateBorrowAllowance = async ( const delegateAllowancePromise = pool .connect(user.signer) - .delegateBorrowAllowance(receiver, reserve, interestRateMode, amountToDelegate.toString()); + .delegateBorrowAllowance(reserve, receiver, interestRateMode, amountToDelegate.toString()); if (expectedResult === 'revert') { await expect(delegateAllowancePromise, revertMessage).to.be.reverted; return;