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, address liquidator,
bool receiveAToken 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) * @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); ) external view returns (bool);
function getReserves() external view returns (address[] memory); 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 {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {Pausable} from '@openzeppelin/contracts/utils/Pausable.sol';
import { import {
VersionedInitializable VersionedInitializable
} from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol'; } from '../libraries/openzeppelin-upgradeability/VersionedInitializable.sol';
@ -31,7 +32,7 @@ import {ILendingPool} from '../interfaces/ILendingPool.sol';
* @author Aave * @author Aave
**/ **/
contract LendingPool is VersionedInitializable, ILendingPool { contract LendingPool is VersionedInitializable, Pausable, ILendingPool {
using SafeMath for uint256; using SafeMath for uint256;
using WadRayMath for uint256; using WadRayMath for uint256;
using ReserveLogic for ReserveLogic.ReserveData; using ReserveLogic for ReserveLogic.ReserveData;
@ -93,7 +94,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 amount, uint256 amount,
address onBehalfOf, address onBehalfOf,
uint16 referralCode uint16 referralCode
) external override { ) external override whenNotPaused {
ReserveLogic.ReserveData storage reserve = _reserves[asset]; ReserveLogic.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateDeposit(reserve, amount); ValidationLogic.validateDeposit(reserve, amount);
@ -121,7 +122,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* @param asset the address of the reserve * @param asset the address of the reserve
* @param amount the underlying amount to be redeemed * @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]; ReserveLogic.ReserveData storage reserve = _reserves[asset];
address aToken = reserve.aTokenAddress; address aToken = reserve.aTokenAddress;
@ -172,7 +173,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 amount, uint256 amount,
uint256 interestRateMode, uint256 interestRateMode,
uint16 referralCode uint16 referralCode
) external override { ) external override whenNotPaused {
_executeBorrow( _executeBorrow(
ExecuteBorrowParams( ExecuteBorrowParams(
asset, asset,
@ -199,7 +200,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 amount, uint256 amount,
uint256 rateMode, uint256 rateMode,
address onBehalfOf address onBehalfOf
) external override { ) external override whenNotPaused {
_executeRepay(asset, msg.sender, amount, rateMode, onBehalfOf); _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 asset the address of the reserve on which the user borrowed
* @param rateMode the rate mode that the user wants to swap * @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]; ReserveLogic.ReserveData storage reserve = _reserves[asset];
(uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); (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 asset the address of the reserve
* @param user the address of the user to be rebalanced * @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]; ReserveLogic.ReserveData storage reserve = _reserves[asset];
IStableDebtToken stableDebtToken = IStableDebtToken(reserve.stableDebtTokenAddress); IStableDebtToken stableDebtToken = IStableDebtToken(reserve.stableDebtTokenAddress);
@ -348,7 +349,11 @@ contract LendingPool is VersionedInitializable, ILendingPool {
* @param asset the address of the reserve * @param asset the address of the reserve
* @param useAsCollateral true if the user wants to user the deposit as collateral, false otherwise. * @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]; ReserveLogic.ReserveData storage reserve = _reserves[asset];
ValidationLogic.validateSetUseReserveAsCollateral( ValidationLogic.validateSetUseReserveAsCollateral(
@ -384,7 +389,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
address user, address user,
uint256 purchaseAmount, uint256 purchaseAmount,
bool receiveAToken bool receiveAToken
) external override { ) external override whenNotPaused {
address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager(); address liquidationManager = _addressesProvider.getLendingPoolLiquidationManager();
//solium-disable-next-line //solium-disable-next-line
@ -440,7 +445,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 principalAmount, uint256 principalAmount,
address receiver, address receiver,
bytes calldata params bytes calldata params
) external override { ) external override whenNotPaused {
require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED); require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED);
_flashLiquidationLocked = true; _flashLiquidationLocked = true;
@ -487,7 +492,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
uint256 mode, uint256 mode,
bytes calldata params, bytes calldata params,
uint16 referralCode uint16 referralCode
) external override { ) external override whenNotPaused {
ReserveLogic.ReserveData storage reserve = _reserves[asset]; ReserveLogic.ReserveData storage reserve = _reserves[asset];
FlashLoanLocalVars memory vars; FlashLoanLocalVars memory vars;
@ -706,7 +711,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
address stableDebtAddress, address stableDebtAddress,
address variableDebtAddress, address variableDebtAddress,
address interestRateStrategyAddress address interestRateStrategyAddress
) external override onlyLendingPoolConfigurator { ) external override onlyLendingPoolConfigurator whenNotPaused {
_reserves[asset].init( _reserves[asset].init(
aTokenAddress, aTokenAddress,
stableDebtAddress, stableDebtAddress,
@ -726,6 +731,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
external external
override override
onlyLendingPoolConfigurator onlyLendingPoolConfigurator
whenNotPaused
{ {
_reserves[asset].interestRateStrategyAddress = rateStrategyAddress; _reserves[asset].interestRateStrategyAddress = rateStrategyAddress;
} }
@ -908,4 +914,25 @@ contract LendingPool is VersionedInitializable, ILendingPool {
function getAddressesProvider() external view returns (ILendingPoolAddressesProvider) { function getAddressesProvider() external view returns (ILendingPoolAddressesProvider) {
return _addressesProvider; 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); 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 amount,
uint256 index uint256 index
) external override onlyLendingPool { ) external override onlyLendingPool {
uint256 currentBalance = balanceOf(user); uint256 currentBalance = balanceOf(user);
require(amount <= currentBalance, Errors.INVALID_ATOKEN_BALANCE); require(amount <= currentBalance, Errors.INVALID_ATOKEN_BALANCE);
@ -79,7 +78,6 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
//transfers the underlying to the target //transfers the underlying to the target
ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount); ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount);
emit Burn(msg.sender, receiverOfUnderlying, amount, index); 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 user the address receiving the minted tokens
* @param amount the amount of tokens to mint * @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); uint256 scaledAmount = amount.rayDiv(index);
//mint an equivalent amount of tokens to cover the new deposit //mint an equivalent amount of tokens to cover the new deposit
_mint(user,scaledAmount); _mint(user, scaledAmount);
emit Mint(user, amount, index); emit Mint(user, amount, index);
} }
@ -150,9 +150,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
return 0; return 0;
} }
return return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
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 * @return true if the user can transfer amount, false otherwise
**/ **/
function isTransferAllowed(address user, uint256 amount) public override view returns (bool) { 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 * @dev transfers the underlying asset to the target. Used by the lendingpool to transfer
* assets in borrow(), redeem() and flashLoan() * assets in borrow(), redeem() and flashLoan()
* @param target the target of the transfer * @param target the target of the transfer
* @param amount the amount to transfer * @param amount the amount to transfer
* @return the amount transferred * @return the amount transferred
**/ **/
function transferUnderlyingTo(address target, uint256 amount) function transferUnderlyingTo(address target, uint256 amount)
external external
override override
@ -187,8 +185,8 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
address to, address to,
uint256 amount, uint256 amount,
bool validate bool validate
) internal { ) internal {
if(validate){ if (validate) {
require(isTransferAllowed(from, amount), Errors.TRANSFER_NOT_ALLOWED); require(isTransferAllowed(from, amount), Errors.TRANSFER_NOT_ALLOWED);
} }
@ -199,17 +197,16 @@ contract AToken is VersionedInitializable, ERC20, IAToken {
super._transfer(from, to, scaledAmount); super._transfer(from, to, scaledAmount);
emit BalanceTransfer(from, to, amount, index); emit BalanceTransfer(from, to, amount, index);
} }
function _transfer( function _transfer(
address from, address from,
address to, address to,
uint256 amount uint256 amount
) internal override { ) internal override {
_transfer(from, to, amount, true);
_transfer(from, to, amount, true);
} }
/** /**
* @dev aTokens should not receive ETH * @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-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-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-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", "test-flash": "buidler test test/__setup.spec.ts test/flashloan.spec.ts",
"dev:coverage": "buidler coverage --network coverage", "dev:coverage": "buidler coverage --network coverage",
"dev:deployment": "buidler dev-deployment", "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 () => { 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 {users, pool, weth} = testEnv;
const userAddress = await pool.signer.getAddress(); const userAddress = await pool.signer.getAddress();
@ -81,4 +80,58 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
).to.be.revertedWith(TRANSFER_NOT_ALLOWED); ).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
);
});
}); });