Skip to content

Commit

Permalink
Merge branch 'main' into develop
Browse files Browse the repository at this point in the history
# Conflicts:
#	deployments/deployed-contracts-rinkeby.json
  • Loading branch information
thorseldon committed Apr 1, 2022
2 parents c334f69 + 1abbb90 commit c87accd
Show file tree
Hide file tree
Showing 14 changed files with 53,911 additions and 10,533 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ node_modules
#Hardhat files
cache
artifacts
storage_layout
Binary file added audits/Certik_Final_Report_2022_04_01.pdf
Binary file not shown.
78 changes: 78 additions & 0 deletions contracts/mock/MaliciousHackerERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

import {ILendPool} from "../interfaces/ILendPool.sol";
import {IDebtToken} from "../interfaces/IDebtToken.sol";
import {DataTypes} from "../libraries/types/DataTypes.sol";

/**
* @title MaliciousHackerERC721
* @dev Malicious Hacker Logic
*/
contract MaliciousHackerERC721 is IERC721Receiver {
ILendPool internal _pool;
uint256 internal _simulateAction;

uint256 public constant ACTION_DEPOSIT = 1;
uint256 public constant ACTION_WITHDRAW = 2;
uint256 public constant ACTION_BORROW = 3;
uint256 public constant ACTION_REPAY = 4;
uint256 public constant ACTION_AUCTION = 5;
uint256 public constant ACTION_REDEEM = 6;
uint256 public constant ACTION_LIQUIDATE = 7;

constructor(address pool_) {
_pool = ILendPool(pool_);
}

function approveDelegate(address reserve, address delegatee) public {
DataTypes.ReserveData memory reserveData = _pool.getReserveData(reserve);
IDebtToken debtToken = IDebtToken(reserveData.debtTokenAddress);

debtToken.approveDelegation(delegatee, type(uint256).max);
}

function simulateAction(uint256 simulateAction_) public {
_simulateAction = simulateAction_;
}

function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external override returns (bytes4) {
operator;
from;
tokenId;
data;

address[] memory reserves = _pool.getReservesList();
address[] memory nfts = _pool.getNftsList();
address onBehalfOf = msg.sender;
address to = msg.sender;
uint16 referralCode = 0;
uint256 amount = 1 ether;
uint256 bidPrice = 2 ether;

if (_simulateAction == ACTION_DEPOSIT) {
_pool.deposit(reserves[0], amount, onBehalfOf, referralCode);
} else if (_simulateAction == ACTION_WITHDRAW) {
_pool.withdraw(reserves[0], amount, to);
} else if (_simulateAction == ACTION_BORROW) {
_pool.borrow(reserves[0], amount, nfts[0], tokenId, onBehalfOf, referralCode);
} else if (_simulateAction == ACTION_REPAY) {
_pool.repay(nfts[0], tokenId, amount);
} else if (_simulateAction == ACTION_AUCTION) {
_pool.auction(nfts[0], tokenId, bidPrice, onBehalfOf);
} else if (_simulateAction == ACTION_REDEEM) {
_pool.redeem(nfts[0], tokenId, amount);
} else if (_simulateAction == ACTION_LIQUIDATE) {
_pool.liquidate(nfts[0], tokenId, amount);
}

return IERC721Receiver.onERC721Received.selector;
}
}
46 changes: 38 additions & 8 deletions contracts/protocol/LendPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {ReserveConfiguration} from "../libraries/configuration/ReserveConfigurat
import {NftConfiguration} from "../libraries/configuration/NftConfiguration.sol";
import {DataTypes} from "../libraries/types/DataTypes.sol";
import {LendPoolStorage} from "./LendPoolStorage.sol";
import {LendPoolStorageExt} from "./LendPoolStorageExt.sol";

import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
Expand All @@ -43,7 +44,15 @@ import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/Cont
* LendPoolAddressesProvider
* @author Bend
**/
contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeable, IERC721ReceiverUpgradeable {
// !!! For Upgradable: DO NOT ADJUST Inheritance Order !!!
contract LendPool is
Initializable,
ILendPool,
LendPoolStorage,
ContextUpgradeable,
IERC721ReceiverUpgradeable,
LendPoolStorageExt
{
using WadRayMath for uint256;
using PercentageMath for uint256;
using SafeERC20Upgradeable for IERC20Upgradeable;
Expand All @@ -52,6 +61,27 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
using NftConfiguration for DataTypes.NftConfigurationMap;

/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

// Any calls to nonReentrant after this point will fail
_status = _ENTERED;

_;

// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}

modifier whenNotPaused() {
_whenNotPaused();
_;
Expand Down Expand Up @@ -100,7 +130,7 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external override whenNotPaused {
) external override nonReentrant whenNotPaused {
require(onBehalfOf != address(0), Errors.VL_INVALID_ONBEHALFOF_ADDRESS);

DataTypes.ReserveData storage reserve = _reserves[asset];
Expand Down Expand Up @@ -134,7 +164,7 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
address asset,
uint256 amount,
address to
) external override whenNotPaused returns (uint256) {
) external override nonReentrant whenNotPaused returns (uint256) {
require(to != address(0), Errors.VL_INVALID_TARGET_ADDRESS);

DataTypes.ReserveData storage reserve = _reserves[asset];
Expand Down Expand Up @@ -193,7 +223,7 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
uint256 nftTokenId,
address onBehalfOf,
uint16 referralCode
) external override whenNotPaused {
) external override nonReentrant whenNotPaused {
require(onBehalfOf != address(0), Errors.VL_INVALID_ONBEHALFOF_ADDRESS);

ExecuteBorrowLocalVars memory vars;
Expand Down Expand Up @@ -289,7 +319,7 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
address nftAsset,
uint256 nftTokenId,
uint256 amount
) external override whenNotPaused returns (uint256, bool) {
) external override nonReentrant whenNotPaused returns (uint256, bool) {
RepayLocalVars memory vars;
vars.initiator = _msgSender();

Expand Down Expand Up @@ -380,7 +410,7 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
uint256 nftTokenId,
uint256 bidPrice,
address onBehalfOf
) external override whenNotPaused {
) external override nonReentrant whenNotPaused {
address poolLiquidator = _addressesProvider.getLendPoolLiquidator();

//solium-disable-next-line
Expand All @@ -402,7 +432,7 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
address nftAsset,
uint256 nftTokenId,
uint256 amount
) external override whenNotPaused returns (uint256) {
) external override nonReentrant whenNotPaused returns (uint256) {
address poolLiquidator = _addressesProvider.getLendPoolLiquidator();

//solium-disable-next-line
Expand All @@ -428,7 +458,7 @@ contract LendPool is Initializable, ILendPool, LendPoolStorage, ContextUpgradeab
address nftAsset,
uint256 nftTokenId,
uint256 amount
) external override whenNotPaused returns (uint256) {
) external override nonReentrant whenNotPaused returns (uint256) {
address poolLiquidator = _addressesProvider.getLendPoolLiquidator();

//solium-disable-next-line
Expand Down
3 changes: 3 additions & 0 deletions contracts/protocol/LendPoolStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ contract LendPoolStorage {

uint256 internal _maxNumberOfReserves;
uint256 internal _maxNumberOfNfts;

// !!! Never add new variable at here, because this contract is inherited by LendPool !!!
// !!! Add new variable at LendPool directly !!!
}
14 changes: 14 additions & 0 deletions contracts/protocol/LendPoolStorageExt.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

contract LendPoolStorageExt {
// !!! Add new variable MUST append it only, do not insert, update type & name, or change order !!!
// https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#potentially-unsafe-operations

uint256 internal constant _NOT_ENTERED = 0;
uint256 internal constant _ENTERED = 1;
uint256 internal _status;

// For upgradable, add one new variable above, minus 1 at here
uint256[49] private __gap;
}
49 changes: 37 additions & 12 deletions contracts/protocol/PunkGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,31 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec

mapping(address => bool) internal _callerWhitelists;

uint256 private constant _NOT_ENTERED = 0;
uint256 private constant _ENTERED = 1;
uint256 private _status;

/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

// Any calls to nonReentrant after this point will fail
_status = _ENTERED;

_;

// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}

function initialize(
address addressProvider,
address wethGateway,
Expand Down Expand Up @@ -60,13 +85,13 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
return ILendPoolLoan(_addressProvider.getLendPoolLoan());
}

function authorizeLendPoolERC20(address[] calldata tokens) external onlyOwner {
function authorizeLendPoolERC20(address[] calldata tokens) external nonReentrant onlyOwner {
for (uint256 i = 0; i < tokens.length; i++) {
IERC20Upgradeable(tokens[i]).approve(address(_getLendPool()), type(uint256).max);
}
}

function authorizeCallerWhitelist(address[] calldata callers, bool flag) external onlyOwner {
function authorizeCallerWhitelist(address[] calldata callers, bool flag) external nonReentrant onlyOwner {
for (uint256 i = 0; i < callers.length; i++) {
_callerWhitelists[callers[i]] = flag;
}
Expand Down Expand Up @@ -106,7 +131,7 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
uint256 punkIndex,
address onBehalfOf,
uint16 referralCode
) external override {
) external override nonReentrant {
_checkValidCallerAndOnBehalfOf(onBehalfOf);

ILendPool cachedPool = _getLendPool();
Expand All @@ -127,7 +152,7 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
punks.transferPunk(onBehalfOf, punkIndex);
}

function repay(uint256 punkIndex, uint256 amount) external override returns (uint256, bool) {
function repay(uint256 punkIndex, uint256 amount) external override nonReentrant returns (uint256, bool) {
ILendPool cachedPool = _getLendPool();
ILendPoolLoan cachedPoolLoan = _getLendPoolLoan();

Expand Down Expand Up @@ -157,7 +182,7 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
uint256 punkIndex,
uint256 bidPrice,
address onBehalfOf
) external override {
) external override nonReentrant {
_checkValidCallerAndOnBehalfOf(onBehalfOf);

ILendPool cachedPool = _getLendPool();
Expand All @@ -173,7 +198,7 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
cachedPool.auction(address(wrappedPunks), punkIndex, bidPrice, onBehalfOf);
}

function redeem(uint256 punkIndex, uint256 amount) external override returns (uint256) {
function redeem(uint256 punkIndex, uint256 amount) external override nonReentrant returns (uint256) {
ILendPool cachedPool = _getLendPool();
ILendPoolLoan cachedPoolLoan = _getLendPoolLoan();

Expand All @@ -193,7 +218,7 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
return paybackAmount;
}

function liquidate(uint256 punkIndex, uint256 amount) external override returns (uint256) {
function liquidate(uint256 punkIndex, uint256 amount) external override nonReentrant returns (uint256) {
ILendPool cachedPool = _getLendPool();
ILendPoolLoan cachedPoolLoan = _getLendPoolLoan();

Expand Down Expand Up @@ -223,14 +248,14 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
uint256 punkIndex,
address onBehalfOf,
uint16 referralCode
) external override {
) external override nonReentrant {
_checkValidCallerAndOnBehalfOf(onBehalfOf);

_depositPunk(punkIndex);
_wethGateway.borrowETH(amount, address(wrappedPunks), punkIndex, onBehalfOf, referralCode);
}

function repayETH(uint256 punkIndex, uint256 amount) external payable override returns (uint256, bool) {
function repayETH(uint256 punkIndex, uint256 amount) external payable override nonReentrant returns (uint256, bool) {
ILendPoolLoan cachedPoolLoan = _getLendPoolLoan();

uint256 loanId = cachedPoolLoan.getCollateralLoanId(address(wrappedPunks), punkIndex);
Expand All @@ -257,13 +282,13 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
return (paybackAmount, burn);
}

function auctionETH(uint256 punkIndex, address onBehalfOf) external payable override {
function auctionETH(uint256 punkIndex, address onBehalfOf) external payable override nonReentrant {
_checkValidCallerAndOnBehalfOf(onBehalfOf);

_wethGateway.auctionETH{value: msg.value}(address(wrappedPunks), punkIndex, onBehalfOf);
}

function redeemETH(uint256 punkIndex, uint256 amount) external payable override returns (uint256) {
function redeemETH(uint256 punkIndex, uint256 amount) external payable override nonReentrant returns (uint256) {
ILendPoolLoan cachedPoolLoan = _getLendPoolLoan();

uint256 loanId = cachedPoolLoan.getCollateralLoanId(address(wrappedPunks), punkIndex);
Expand All @@ -281,7 +306,7 @@ contract PunkGateway is IPunkGateway, ERC721HolderUpgradeable, EmergencyTokenRec
return paybackAmount;
}

function liquidateETH(uint256 punkIndex) external payable override returns (uint256) {
function liquidateETH(uint256 punkIndex) external payable override nonReentrant returns (uint256) {
ILendPoolLoan cachedPoolLoan = _getLendPoolLoan();

uint256 loanId = cachedPoolLoan.getCollateralLoanId(address(wrappedPunks), punkIndex);
Expand Down
Loading

0 comments on commit c87accd

Please sign in to comment.