mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
initial implementation of the credit delegation + basic tests
This commit is contained in:
parent
23f99d30f0
commit
3173bee782
|
@ -28,6 +28,12 @@ interface ILendingPool {
|
||||||
**/
|
**/
|
||||||
event Withdraw(address indexed reserve, address indexed user, uint256 amount);
|
event Withdraw(address indexed reserve, address indexed user, uint256 amount);
|
||||||
|
|
||||||
|
event BorrowAllowanceDelegated(
|
||||||
|
address indexed fromUser,
|
||||||
|
address indexed toUser,
|
||||||
|
address indexed asset,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* @dev emitted on borrow
|
* @dev emitted on borrow
|
||||||
* @param reserve the address of the reserve
|
* @param reserve the address of the reserve
|
||||||
|
@ -39,7 +45,8 @@ interface ILendingPool {
|
||||||
**/
|
**/
|
||||||
event Borrow(
|
event Borrow(
|
||||||
address indexed reserve,
|
address indexed reserve,
|
||||||
address indexed user,
|
address user,
|
||||||
|
address indexed onBehalfOf,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 borrowRateMode,
|
uint256 borrowRateMode,
|
||||||
uint256 borrowRate,
|
uint256 borrowRate,
|
||||||
|
@ -149,6 +156,18 @@ interface ILendingPool {
|
||||||
**/
|
**/
|
||||||
function withdraw(address reserve, uint256 amount) external;
|
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
|
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
|
||||||
* already deposited enough collateral.
|
* already deposited enough collateral.
|
||||||
|
@ -160,7 +179,8 @@ interface ILendingPool {
|
||||||
address reserve,
|
address reserve,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 interestRateMode,
|
uint256 interestRateMode,
|
||||||
uint16 referralCode
|
uint16 referralCode,
|
||||||
|
address onBehalfOf
|
||||||
) external;
|
) external;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -48,7 +48,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
|
|
||||||
mapping(address => ReserveLogic.ReserveData) internal _reserves;
|
mapping(address => ReserveLogic.ReserveData) internal _reserves;
|
||||||
mapping(address => UserConfiguration.Map) internal _usersConfig;
|
mapping(address => UserConfiguration.Map) internal _usersConfig;
|
||||||
|
mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance;
|
||||||
address[] internal _reservesList;
|
address[] internal _reservesList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,6 +157,23 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
emit Withdraw(asset, msg.sender, amount);
|
emit Withdraw(asset, msg.sender, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBorrowAllowance(
|
||||||
|
address fromUser,
|
||||||
|
address toUser,
|
||||||
|
address asset
|
||||||
|
) 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
|
* @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
|
||||||
* already deposited enough collateral.
|
* already deposited enough collateral.
|
||||||
|
@ -169,12 +186,19 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
address asset,
|
address asset,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 interestRateMode,
|
uint256 interestRateMode,
|
||||||
uint16 referralCode
|
uint16 referralCode,
|
||||||
|
address onBehalfOf
|
||||||
) external override {
|
) 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(
|
_executeBorrow(
|
||||||
ExecuteBorrowParams(
|
ExecuteBorrowParams(
|
||||||
asset,
|
asset,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
|
onBehalfOf,
|
||||||
amount,
|
amount,
|
||||||
interestRateMode,
|
interestRateMode,
|
||||||
_reserves[asset].aTokenAddress,
|
_reserves[asset].aTokenAddress,
|
||||||
|
@ -450,21 +474,20 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
vars.amountPlusPremium = amount.add(vars.premium);
|
vars.amountPlusPremium = amount.add(vars.premium);
|
||||||
|
|
||||||
if (debtMode == ReserveLogic.InterestRateMode.NONE) {
|
if (debtMode == ReserveLogic.InterestRateMode.NONE) {
|
||||||
|
|
||||||
IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium);
|
IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium);
|
||||||
|
|
||||||
reserve.updateCumulativeIndexesAndTimestamp();
|
reserve.updateCumulativeIndexesAndTimestamp();
|
||||||
reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium);
|
reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium);
|
||||||
reserve.updateInterestRates(asset, vars.aTokenAddress, vars.premium, 0);
|
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 {
|
} else {
|
||||||
// If the transfer didn't succeed, the receiver either didn't return the funds, or didn't approve the transfer.
|
// If the transfer didn't succeed, the receiver either didn't return the funds, or didn't approve the transfer.
|
||||||
_executeBorrow(
|
_executeBorrow(
|
||||||
ExecuteBorrowParams(
|
ExecuteBorrowParams(
|
||||||
asset,
|
asset,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
|
msg.sender,
|
||||||
vars.amountPlusPremium.sub(vars.availableBalance),
|
vars.amountPlusPremium.sub(vars.availableBalance),
|
||||||
mode,
|
mode,
|
||||||
vars.aTokenAddress,
|
vars.aTokenAddress,
|
||||||
|
@ -694,6 +717,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
struct ExecuteBorrowParams {
|
struct ExecuteBorrowParams {
|
||||||
address asset;
|
address asset;
|
||||||
address user;
|
address user;
|
||||||
|
address onBehalfOf;
|
||||||
uint256 amount;
|
uint256 amount;
|
||||||
uint256 interestRateMode;
|
uint256 interestRateMode;
|
||||||
address aTokenAddress;
|
address aTokenAddress;
|
||||||
|
@ -707,7 +731,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
**/
|
**/
|
||||||
function _executeBorrow(ExecuteBorrowParams memory vars) internal {
|
function _executeBorrow(ExecuteBorrowParams memory vars) internal {
|
||||||
ReserveLogic.ReserveData storage reserve = _reserves[vars.asset];
|
ReserveLogic.ReserveData storage reserve = _reserves[vars.asset];
|
||||||
UserConfiguration.Map storage userConfig = _usersConfig[msg.sender];
|
UserConfiguration.Map storage userConfig = _usersConfig[vars.onBehalfOf];
|
||||||
|
|
||||||
address oracle = _addressesProvider.getPriceOracle();
|
address oracle = _addressesProvider.getPriceOracle();
|
||||||
|
|
||||||
|
@ -717,6 +741,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
|
|
||||||
ValidationLogic.validateBorrow(
|
ValidationLogic.validateBorrow(
|
||||||
reserve,
|
reserve,
|
||||||
|
vars.onBehalfOf,
|
||||||
vars.asset,
|
vars.asset,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
amountInETH,
|
amountInETH,
|
||||||
|
@ -728,13 +753,11 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
oracle
|
oracle
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
uint256 reserveIndex = reserve.index;
|
uint256 reserveIndex = reserve.index;
|
||||||
if (!userConfig.isBorrowing(reserveIndex)) {
|
if (!userConfig.isBorrowing(reserveIndex)) {
|
||||||
userConfig.setBorrowing(reserveIndex, true);
|
userConfig.setBorrowing(reserveIndex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
reserve.updateCumulativeIndexesAndTimestamp();
|
reserve.updateCumulativeIndexesAndTimestamp();
|
||||||
|
|
||||||
//caching the current stable borrow rate
|
//caching the current stable borrow rate
|
||||||
|
@ -746,24 +769,29 @@ contract LendingPool is VersionedInitializable, ILendingPool {
|
||||||
currentStableRate = reserve.currentStableBorrowRate;
|
currentStableRate = reserve.currentStableBorrowRate;
|
||||||
|
|
||||||
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
||||||
vars.user,
|
vars.onBehalfOf,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
currentStableRate
|
currentStableRate
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.user, vars.amount);
|
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.onBehalfOf, vars.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve.updateInterestRates(vars.asset, vars.aTokenAddress, 0, vars.releaseUnderlying ? vars.amount : 0);
|
reserve.updateInterestRates(
|
||||||
|
vars.asset,
|
||||||
if(vars.releaseUnderlying){
|
vars.aTokenAddress,
|
||||||
IAToken(vars.aTokenAddress).transferUnderlyingTo(msg.sender, vars.amount);
|
0,
|
||||||
|
vars.releaseUnderlying ? vars.amount : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (vars.releaseUnderlying) {
|
||||||
|
IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
emit Borrow(
|
emit Borrow(
|
||||||
vars.asset,
|
vars.asset,
|
||||||
msg.sender,
|
vars.user,
|
||||||
|
vars.onBehalfOf,
|
||||||
vars.amount,
|
vars.amount,
|
||||||
vars.interestRateMode,
|
vars.interestRateMode,
|
||||||
ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
|
ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
|
||||||
|
|
|
@ -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 INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
|
||||||
string public constant CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The actual balance of the protocol is inconsistent'
|
string public constant CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The actual balance of the protocol is inconsistent'
|
||||||
string public constant INVALID_FLASHLOAN_MODE = '43'; //Invalid flashloan mode selected
|
string public constant INVALID_FLASHLOAN_MODE = '43'; //Invalid flashloan mode selected
|
||||||
|
string public constant BORROW_ALLOWANCE_ARE_NOT_ENOUGH = '52'; // User borrows on behalf, but allowance are too small
|
||||||
|
|
||||||
// require error messages - aToken
|
// require error messages - aToken
|
||||||
string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'
|
string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'
|
||||||
|
@ -70,7 +71,7 @@ library Errors {
|
||||||
string public constant NO_ERRORS = '42'; // 'No errors'
|
string public constant NO_ERRORS = '42'; // 'No errors'
|
||||||
|
|
||||||
//require error messages - Math libraries
|
//require error messages - Math libraries
|
||||||
string public constant MULTIPLICATION_OVERFLOW = '44';
|
string public constant MULTIPLICATION_OVERFLOW = '44';
|
||||||
string public constant ADDITION_OVERFLOW = '45';
|
string public constant ADDITION_OVERFLOW = '45';
|
||||||
string public constant DIVISION_BY_ZERO = '46';
|
string public constant DIVISION_BY_ZERO = '46';
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ library ValidationLogic {
|
||||||
/**
|
/**
|
||||||
* @dev validates a borrow.
|
* @dev validates a borrow.
|
||||||
* @param reserve the reserve state from which the user is borrowing
|
* @param reserve the reserve state from which the user is borrowing
|
||||||
|
* @param userAddress the address of the user
|
||||||
* @param reserveAddress the address of the reserve
|
* @param reserveAddress the address of the reserve
|
||||||
* @param amount the amount to be borrowed
|
* @param amount the amount to be borrowed
|
||||||
* @param amountInETH the amount to be borrowed, in ETH
|
* @param amountInETH the amount to be borrowed, in ETH
|
||||||
|
@ -113,6 +114,7 @@ library ValidationLogic {
|
||||||
|
|
||||||
function validateBorrow(
|
function validateBorrow(
|
||||||
ReserveLogic.ReserveData storage reserve,
|
ReserveLogic.ReserveData storage reserve,
|
||||||
|
address userAddress,
|
||||||
address reserveAddress,
|
address reserveAddress,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 amountInETH,
|
uint256 amountInETH,
|
||||||
|
@ -151,7 +153,7 @@ library ValidationLogic {
|
||||||
vars.currentLiquidationThreshold,
|
vars.currentLiquidationThreshold,
|
||||||
vars.healthFactor
|
vars.healthFactor
|
||||||
) = GenericLogic.calculateUserAccountData(
|
) = GenericLogic.calculateUserAccountData(
|
||||||
msg.sender,
|
userAddress,
|
||||||
reservesData,
|
reservesData,
|
||||||
userConfig,
|
userConfig,
|
||||||
reserves,
|
reserves,
|
||||||
|
@ -192,7 +194,7 @@ library ValidationLogic {
|
||||||
require(
|
require(
|
||||||
!userConfig.isUsingAsCollateral(reserve.index) ||
|
!userConfig.isUsingAsCollateral(reserve.index) ||
|
||||||
reserve.configuration.getLtv() == 0 ||
|
reserve.configuration.getLtv() == 0 ||
|
||||||
amount > IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
|
amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress),
|
||||||
Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
|
Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -321,10 +323,10 @@ library ValidationLogic {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev validates a flashloan action
|
* @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 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
|
* @param premium the premium paid on the flashloan
|
||||||
**/
|
**/
|
||||||
function validateFlashloan(uint256 mode, uint256 premium) internal pure {
|
function validateFlashloan(uint256 mode, uint256 premium) internal pure {
|
||||||
require(premium > 0, Errors.REQUESTED_AMOUNT_TOO_SMALL);
|
require(premium > 0, Errors.REQUESTED_AMOUNT_TOO_SMALL);
|
||||||
require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE);
|
require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE);
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
},
|
},
|
||||||
"DefaultReserveInterestRateStrategy": {
|
"DefaultReserveInterestRateStrategy": {
|
||||||
"buidlerevm": {
|
"buidlerevm": {
|
||||||
"address": "0x626FdE749F9d499d3777320CAf29484B624ab84a",
|
"address": "0x2530ce07D254eA185E8e0bCC37a39e2FbA3bE548",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
|
@ -422,7 +422,7 @@
|
||||||
},
|
},
|
||||||
"StableDebtToken": {
|
"StableDebtToken": {
|
||||||
"buidlerevm": {
|
"buidlerevm": {
|
||||||
"address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6",
|
"address": "0x0Cf45557d25a4e4c0F1aC65EF6c48ae67c61a0E6",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
|
@ -432,7 +432,7 @@
|
||||||
},
|
},
|
||||||
"VariableDebtToken": {
|
"VariableDebtToken": {
|
||||||
"buidlerevm": {
|
"buidlerevm": {
|
||||||
"address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d",
|
"address": "0x7fAeC7791277Ff512c41CA903c177B2Ed952dDAc",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"localhost": {
|
"localhost": {
|
||||||
|
@ -446,7 +446,7 @@
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
},
|
},
|
||||||
"buidlerevm": {
|
"buidlerevm": {
|
||||||
"address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E",
|
"address": "0x33958cC3535Fc328369EAC2B2Bebd120D67C7fa1",
|
||||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -103,7 +103,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
||||||
await expect(
|
await expect(
|
||||||
pool
|
pool
|
||||||
.connect(users[1].signer)
|
.connect(users[1].signer)
|
||||||
.borrow(weth.address, ethers.utils.parseEther('0.1'), RateMode.Stable, AAVE_REFERRAL),
|
.borrow(
|
||||||
|
weth.address,
|
||||||
|
ethers.utils.parseEther('0.1'),
|
||||||
|
RateMode.Stable,
|
||||||
|
AAVE_REFERRAL,
|
||||||
|
users[1].address
|
||||||
|
),
|
||||||
COLLATERAL_BALANCE_IS_0
|
COLLATERAL_BALANCE_IS_0
|
||||||
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
|
).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
|
||||||
});
|
});
|
||||||
|
@ -116,7 +122,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(users[1].signer)
|
.connect(users[1].signer)
|
||||||
.borrow(weth.address, ethers.utils.parseEther('0.1'), RateMode.Stable, AAVE_REFERRAL);
|
.borrow(
|
||||||
|
weth.address,
|
||||||
|
ethers.utils.parseEther('0.1'),
|
||||||
|
RateMode.Stable,
|
||||||
|
AAVE_REFERRAL,
|
||||||
|
users[1].address
|
||||||
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
|
aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
|
||||||
|
|
|
@ -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 (
|
export const borrow = async (
|
||||||
reserveSymbol: string,
|
reserveSymbol: string,
|
||||||
amount: string,
|
amount: string,
|
||||||
interestRateMode: string,
|
interestRateMode: string,
|
||||||
user: SignerWithAddress,
|
user: SignerWithAddress,
|
||||||
|
onBehalfOf: tEthereumAddress,
|
||||||
timeTravel: string,
|
timeTravel: string,
|
||||||
expectedResult: string,
|
expectedResult: string,
|
||||||
testEnv: TestEnv,
|
testEnv: TestEnv,
|
||||||
|
@ -294,15 +317,18 @@ export const borrow = async (
|
||||||
|
|
||||||
const {reserveData: reserveDataBefore, userData: userDataBefore} = await getContractsData(
|
const {reserveData: reserveDataBefore, userData: userDataBefore} = await getContractsData(
|
||||||
reserve,
|
reserve,
|
||||||
user.address,
|
onBehalfOf,
|
||||||
testEnv
|
testEnv,
|
||||||
|
user.address
|
||||||
);
|
);
|
||||||
|
|
||||||
const amountToBorrow = await convertToCurrencyDecimals(reserve, amount);
|
const amountToBorrow = await convertToCurrencyDecimals(reserve, amount);
|
||||||
|
|
||||||
if (expectedResult === 'success') {
|
if (expectedResult === 'success') {
|
||||||
const txResult = await waitForTx(
|
const txResult = await waitForTx(
|
||||||
await pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0')
|
await pool
|
||||||
|
.connect(user.signer)
|
||||||
|
.borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf)
|
||||||
);
|
);
|
||||||
|
|
||||||
const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult);
|
const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult);
|
||||||
|
@ -317,7 +343,7 @@ export const borrow = async (
|
||||||
reserveData: reserveDataAfter,
|
reserveData: reserveDataAfter,
|
||||||
userData: userDataAfter,
|
userData: userDataAfter,
|
||||||
timestamp,
|
timestamp,
|
||||||
} = await getContractsData(reserve, user.address, testEnv);
|
} = await getContractsData(reserve, onBehalfOf, testEnv, user.address);
|
||||||
|
|
||||||
const expectedReserveData = calcExpectedReserveDataAfterBorrow(
|
const expectedReserveData = calcExpectedReserveDataAfterBorrow(
|
||||||
amountToBorrow.toString(),
|
amountToBorrow.toString(),
|
||||||
|
@ -364,7 +390,7 @@ export const borrow = async (
|
||||||
// });
|
// });
|
||||||
} else if (expectedResult === 'revert') {
|
} else if (expectedResult === 'revert') {
|
||||||
await expect(
|
await expect(
|
||||||
pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0'),
|
pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf),
|
||||||
revertMessage
|
revertMessage
|
||||||
).to.be.reverted;
|
).to.be.reverted;
|
||||||
}
|
}
|
||||||
|
@ -845,10 +871,15 @@ const getTxCostAndTimestamp = async (tx: ContractReceipt) => {
|
||||||
return {txCost, txTimestamp};
|
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 {pool} = testEnv;
|
||||||
const reserveData = await getReserveData(pool, reserve);
|
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();
|
const timestamp = await timeLatest();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
redirectInterestStream,
|
redirectInterestStream,
|
||||||
redirectInterestStreamOf,
|
redirectInterestStreamOf,
|
||||||
allowInterestRedirectionTo,
|
allowInterestRedirectionTo,
|
||||||
|
delegateBorrowAllowance,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {RateMode} from '../../helpers/types';
|
import {RateMode} from '../../helpers/types';
|
||||||
|
|
||||||
|
@ -102,6 +103,18 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'delegateBorrowAllowance':
|
||||||
|
{
|
||||||
|
const {amount, toUser: toUserIndex} = action.args;
|
||||||
|
const toUser = users[parseInt(toUserIndex, 10)].address;
|
||||||
|
if (!amount || amount === '') {
|
||||||
|
throw `Invalid amount to deposit into the ${reserve} reserve`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await delegateBorrowAllowance(reserve, amount, user, toUser, testEnv);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'withdraw':
|
case 'withdraw':
|
||||||
{
|
{
|
||||||
const {amount} = action.args;
|
const {amount} = action.args;
|
||||||
|
@ -115,13 +128,27 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
||||||
break;
|
break;
|
||||||
case 'borrow':
|
case 'borrow':
|
||||||
{
|
{
|
||||||
const {amount, timeTravel} = action.args;
|
const {amount, timeTravel, onBehalfOf: onBehalfOfIndex} = action.args;
|
||||||
|
|
||||||
|
const onBehalfOf = onBehalfOfIndex
|
||||||
|
? users[parseInt(onBehalfOfIndex)].address
|
||||||
|
: user.address;
|
||||||
|
|
||||||
if (!amount || amount === '') {
|
if (!amount || amount === '') {
|
||||||
throw `Invalid amount to borrow from the ${reserve} reserve`;
|
throw `Invalid amount to borrow from the ${reserve} reserve`;
|
||||||
}
|
}
|
||||||
|
|
||||||
await borrow(reserve, amount, rateMode, user, timeTravel, expected, testEnv, revertMessage);
|
await borrow(
|
||||||
|
reserve,
|
||||||
|
amount,
|
||||||
|
rateMode,
|
||||||
|
user,
|
||||||
|
onBehalfOf,
|
||||||
|
timeTravel,
|
||||||
|
expected,
|
||||||
|
testEnv,
|
||||||
|
revertMessage
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -955,6 +955,85 @@
|
||||||
"expected": "success"
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,8 @@ export const getReserveData = async (
|
||||||
export const getUserData = async (
|
export const getUserData = async (
|
||||||
pool: LendingPool,
|
pool: LendingPool,
|
||||||
reserve: string,
|
reserve: string,
|
||||||
user: string
|
user: tEthereumAddress,
|
||||||
|
sender?: tEthereumAddress
|
||||||
): Promise<UserReserveData> => {
|
): Promise<UserReserveData> => {
|
||||||
const [userData, aTokenData] = await Promise.all([
|
const [userData, aTokenData] = await Promise.all([
|
||||||
pool.getUserReserveData(reserve, user),
|
pool.getUserReserveData(reserve, user),
|
||||||
|
@ -77,7 +78,7 @@ export const getUserData = async (
|
||||||
] = aTokenData;
|
] = aTokenData;
|
||||||
|
|
||||||
const token = await getMintableErc20(reserve);
|
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 {
|
return {
|
||||||
principalATokenBalance: new BigNumber(principalATokenBalance),
|
principalATokenBalance: new BigNumber(principalATokenBalance),
|
||||||
|
|
|
@ -59,7 +59,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0');
|
.borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address);
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
|
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
|
||||||
|
|
||||||
//drops HF below 1
|
//drops HF below 1
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0');
|
.borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address);
|
||||||
|
|
||||||
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
|
||||||
|
|
||||||
await pool
|
await pool
|
||||||
.connect(borrower.signer)
|
.connect(borrower.signer)
|
||||||
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
|
.borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
|
||||||
|
|
||||||
//drops HF below 1
|
//drops HF below 1
|
||||||
await oracle.setAssetPrice(
|
await oracle.setAssetPrice(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user