Added pausable to Pool actions and aTokens at transfer, triggered by LendingPoolConfigurator. Added basic test to aToken transfer.

This commit is contained in:
David Racero 2020-09-14 13:03:39 +02:00
parent 4b8962d38f
commit bbc11eb092
6 changed files with 145 additions and 38 deletions

View File

@ -129,6 +129,15 @@ interface ILendingPool {
address liquidator,
bool receiveAToken
);
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev deposits The underlying asset into the reserve. A corresponding amount of the overlying asset (aTokens)
@ -374,4 +383,10 @@ interface ILendingPool {
) external view returns (bool);
function getReserves() external view returns (address[] memory);
function pause() external;
function unpause() external;
function isPaused() external view returns (bool);
}

View File

@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {Pausable} from '@openzeppelin/contracts/utils/Pausable.sol';
import {
VersionedInitializable
} from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
@ -31,7 +32,7 @@ import {ILendingPool} from '../interfaces/ILendingPool.sol';
* @author Aave
**/
contract LendingPool is VersionedInitializable, ILendingPool {
contract LendingPool is VersionedInitializable, Pausable, ILendingPool {
using SafeMath for uint256;
using WadRayMath for uint256;
using ReserveLogic for ReserveLogic.ReserveData;
@ -93,7 +94,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external override {
) external override whenNotPaused {
ReserveLogic.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateDeposit(reserve, amount);
@ -121,7 +122,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* @param asset the address of the reserve
* @param amount the underlying amount to be redeemed
**/
function withdraw(address asset, uint256 amount) external override {
function withdraw(address asset, uint256 amount) external override whenNotPaused {
ReserveLogic.ReserveData storage reserve = _reserves[asset];
address aToken = reserve.aTokenAddress;
@ -172,7 +173,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 amount,
uint256 interestRateMode,
uint16 referralCode
) external override {
) external override whenNotPaused {
_executeBorrow(
ExecuteBorrowParams(
asset,
@ -199,7 +200,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external override {
) external override whenNotPaused {
_executeRepay(asset, msg.sender, amount, rateMode, onBehalfOf);
}
@ -260,7 +261,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* @param asset the address of the reserve on which the user borrowed
* @param rateMode the rate mode that the user wants to swap
**/
function swapBorrowRateMode(address asset, uint256 rateMode) external override {
function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused {
ReserveLogic.ReserveData storage reserve = _reserves[asset];
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve);
@ -303,7 +304,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* @param asset the address of the reserve
* @param user the address of the user to be rebalanced
**/
function rebalanceStableBorrowRate(address asset, address user) external override {
function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
ReserveLogic.ReserveData storage reserve = _reserves[asset];
IStableDebtToken stableDebtToken = IStableDebtToken(reserve.stableDebtTokenAddress);
@ -348,7 +349,11 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* @param asset the address of the reserve
* @param useAsCollateral true if the user wants to user the deposit as collateral, false otherwise.
**/
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override {
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral)
external
override
whenNotPaused
{
ReserveLogic.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateSetUseReserveAsCollateral(
@ -384,7 +389,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
address user,
uint256 purchaseAmount,
bool receiveAToken
) external override {
) external override whenNotPaused {
address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager();
//solium-disable-next-line
@ -440,7 +445,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 principalAmount,
address receiver,
bytes calldata params
) external override {
) external override whenNotPaused {
require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED);
_flashLiquidationLocked = true;
@ -487,7 +492,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 mode,
bytes calldata params,
uint16 referralCode
) external override {
) external override whenNotPaused {
ReserveLogic.ReserveData storage reserve = _reserves[asset];
FlashLoanLocalVars memory vars;
@ -706,7 +711,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
address stableDebtAddress,
address variableDebtAddress,
address interestRateStrategyAddress
) external override onlyLendingPoolConfigurator {
) external override onlyLendingPoolConfigurator whenNotPaused {
_reserves[asset].init(
aTokenAddress,
stableDebtAddress,
@ -726,6 +731,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
external
override
onlyLendingPoolConfigurator
whenNotPaused
{
_reserves[asset].interestRateStrategyAddress = rateStrategyAddress;
}
@ -908,4 +914,25 @@ contract LendingPool is VersionedInitializable, ILendingPool {
function getAddressesProvider() external view returns (ILendingPoolAddressesProvider) {
return _addressesProvider;
}
/**
* @dev pause all the Lending Pool actions
*/
function pause() external override onlyLendingPoolConfigurator {
_pause();
}
/**
* @dev unpause all the Lending Pool actions
*/
function unpause() external override onlyLendingPoolConfigurator {
_unpause();
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function isPaused() public override view returns (bool) {
return Pausable.paused();
}
}

View File

@ -582,4 +582,18 @@ contract LendingPoolConfigurator is VersionedInitializable {
proxy.upgradeToAndCall(implementation, params);
}
/**
* @dev pauses LendingPool actions
**/
function pausePool() external onlyLendingPoolManager {
pool.pause();
}
/**
* @dev unpauses LendingPool actions
**/
function unpausePool() external onlyLendingPoolManager {
pool.unpause();
}
}

View File

@ -67,7 +67,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
uint256 amount,
uint256 index
) external override onlyLendingPool {
uint256 currentBalance = balanceOf(user);
require(amount <= currentBalance, Errors.INVALID_ATOKEN_BALANCE);
@ -79,7 +78,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
//transfers the underlying to the target
ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount);
emit Burn(msg.sender, receiverOfUnderlying, amount, index);
}
@ -89,13 +87,15 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
* @param user the address receiving the minted tokens
* @param amount the amount of tokens to mint
*/
function mint(address user, uint256 amount, uint256 index) external override onlyLendingPool {
function mint(
address user,
uint256 amount,
uint256 index
) external override onlyLendingPool {
uint256 scaledAmount = amount.rayDiv(index);
//mint an equivalent amount of tokens to cover the new deposit
_mint(user,scaledAmount);
_mint(user, scaledAmount);
emit Mint(user, amount, index);
}
@ -119,7 +119,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
/**
* @dev calculates the balance of the user, which is the
* principal balance + interest generated by the principal balance
* principal balance + interest generated by the principal balance
* @param user the user for which the balance is being calculated
* @return the total balance of the user
**/
@ -150,9 +150,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
return 0;
}
return
currentSupplyScaled
.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
}
/**
@ -162,16 +160,16 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
* @return true if the user can transfer amount, false otherwise
**/
function isTransferAllowed(address user, uint256 amount) public override view returns (bool) {
return POOL.balanceDecreaseAllowed(UNDERLYING_ASSET_ADDRESS, user, amount);
return !POOL.isPaused() && POOL.balanceDecreaseAllowed(UNDERLYING_ASSET_ADDRESS, user, amount);
}
/**
* @dev transfers the underlying asset to the target. Used by the lendingpool to transfer
* assets in borrow(), redeem() and flashLoan()
* @param target the target of the transfer
* @param amount the amount to transfer
* @return the amount transferred
**/
* @dev transfers the underlying asset to the target. Used by the lendingpool to transfer
* assets in borrow(), redeem() and flashLoan()
* @param target the target of the transfer
* @param amount the amount to transfer
* @return the amount transferred
**/
function transferUnderlyingTo(address target, uint256 amount)
external
override
@ -187,8 +185,8 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
address to,
uint256 amount,
bool validate
) internal {
if(validate){
) internal {
if (validate) {
require(isTransferAllowed(from, amount), Errors.TRANSFER_NOT_ALLOWED);
}
@ -199,17 +197,16 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
super._transfer(from, to, scaledAmount);
emit BalanceTransfer(from, to, amount, index);
}
function _transfer(
address from,
address to,
uint256 amount
) internal override {
_transfer(from, to, amount, true);
) internal override {
_transfer(from, to, amount, true);
}
/**
* @dev aTokens should not receive ETH
**/

View File

@ -15,6 +15,7 @@
"test-scenarios": "buidler test test/__setup.spec.ts test/scenario.spec.ts",
"test-repay-with-collateral": "buidler test test/__setup.spec.ts test/repay-with-collateral.spec.ts",
"test-liquidate-with-collateral": "buidler test test/__setup.spec.ts test/flash-liquidation-with-collateral.spec.ts",
"test-transfers": "buidler test test/__setup.spec.ts test/atoken-transfer.spec.ts",
"test-flash": "buidler test test/__setup.spec.ts test/flashloan.spec.ts",
"dev:coverage": "buidler coverage --network coverage",
"dev:deployment": "buidler dev-deployment",

View File

@ -45,7 +45,6 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
);
});
it('User 0 deposits 1 WETH and user 1 tries to borrow, but the aTokens received as a transfer are not available as collateral (revert expected)', async () => {
const {users, pool, weth} = testEnv;
const userAddress = await pool.signer.getAddress();
@ -81,4 +80,58 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
).to.be.revertedWith(TRANSFER_NOT_ALLOWED);
});
it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succees', async () => {
const {users, pool, dai, aDai, configurator} = testEnv;
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');
await dai.connect(users[0].signer).mint(amountDAItoDeposit);
// user 0 deposits 1000 DAI
await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
await pool
.connect(users[0].signer)
.deposit(dai.address, amountDAItoDeposit, users[0].address, '0');
const user0Balance = await aDai.balanceOf(users[0].address);
const user1Balance = await aDai.balanceOf(users[1].address);
// Configurator pauses the pool
await configurator.pausePool();
// User 0 tries the transfer to User 1
await expect(
aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit)
).to.revertedWith(TRANSFER_NOT_ALLOWED);
const pausedFromBalance = await aDai.balanceOf(users[0].address);
const pausedToBalance = await aDai.balanceOf(users[1].address);
expect(pausedFromBalance).to.be.equal(
user0Balance.toString(),
INVALID_TO_BALANCE_AFTER_TRANSFER
);
expect(pausedToBalance.toString()).to.be.equal(
user1Balance.toString(),
INVALID_FROM_BALANCE_AFTER_TRANSFER
);
// Configurator unpauses the pool
await configurator.unpausePool();
// User 0 succeeds transfer to User 1
await aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit);
const fromBalance = await aDai.balanceOf(users[0].address);
const toBalance = await aDai.balanceOf(users[1].address);
expect(fromBalance.toString()).to.be.equal(
user0Balance.sub(amountDAItoDeposit),
INVALID_FROM_BALANCE_AFTER_TRANSFER
);
expect(toBalance.toString()).to.be.equal(
user1Balance.add(amountDAItoDeposit),
INVALID_TO_BALANCE_AFTER_TRANSFER
);
});
});