Platform: Code4rena
Start Date: 28/11/2022
Pot Size: $192,500 USDC
Total HM: 33
Participants: 106
Period: 11 days
Judge: LSDan
Total Solo HM: 15
Id: 186
League: ETH
Rank: 28/106
Findings: 1
Award: $882.55
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x4non, 0x52, 0xAgro, 0xNazgul, 0xSmartContract, 0xackermann, 9svR6w, Awesome, Aymen0909, B2, BRONZEDISC, Bnke0x0, Deekshith99, Deivitto, Diana, Dravee, HE1M, Jeiwan, Kaiziron, KingNFT, Lambda, Mukund, PaludoX0, RaymondFam, Rolezn, Sathish9098, Secureverse, SmartSek, __141345__, ahmedov, ayeslick, brgltd, cccz, ch0bu, chrisdior4, cryptonue, cryptostellar5, csanuragjain, datapunk, delfin454000, erictee, gz627, gzeon, helios, i_got_hacked, ignacio, imare, jadezti, jayphbee, joestakey, kankodu, ksk2345, ladboy233, martin, nadin, nicobevi, oyc_109, pashov, pavankv, pedr02b2, pzeus, rbserver, ronnyx2017, rvierdiiev, shark, unforgiven, xiaoming90, yjrwkk
882.5476 USDC - $882.55
Issue | Description | Instances |
---|---|---|
1 | Unused/empty receive() function | 1 |
2 | Open TODO re incentives | 1 |
3 | Outdated Open Zeppelin versions | 1 |
Total | 3 |
Issue | Description | Instances |
---|---|---|
1 | Constant value definitions including call to keccak256 should use immutable | 1 |
2 | nonReentrant modifier should appear before all other modifiers | 25 |
3 | Missing NatSpec for some constructors | 7 |
4 | Missing NatSpec for event | 1 |
5 | Partially missing NatSpec for some functions | 24 |
6 | Completely missing NatSpec for some functions | 66 |
7 | Long single-line comments | 17 |
8 | Typos | 9 |
9 | Unclear comment | 1 |
10 | Inconsistent comment // spacing | multiple |
11 | Solidity version should be upgraded | 1 |
12 | TODOs and other open items | 4 |
Total | > 156 |
No. | Explanation + instances |
---|---|
1 | Unused/empty receive() function` |
Leaving the receive() below without a revert could result in a loss of funds for a user who sends Ether to the contract (having no way to get anything back) |
receive() external payable {}
No. | Explanation + instances |
---|---|
2 | Open TODO re incentives |
Open items re incentives such as the TODO below should be addressed before deployment |
makerAsk.price, // TODO: take minPercentageToAsk into account
No. | Explanation + instances |
---|---|
3 | Outdated Open Zeppelin versions |
Outdated Open Zeppelin version are used (v4.7.0 and earlier) | |
The versions used have known vulnerabilities, including three with high severity. See Security Advisories. |
No. | Description |
---|---|
1 | Constant value definitions including call to keccak256 should use immutable |
Constant value definitions such as the call to keccak256 below should use immutable instead of constant |
bytes32 public constant UPDATER_ROLE = keccak256("UPDATER_ROLE");
No. | Description |
---|---|
2 | nonReentrant modifier should appear before all other modifiers |
Adopting this rule protects against the possibilty of reentrancy in other modifiers |
MintableIncentivizedERC721.sol: L458-462
function setIsUsedAsCollateral( uint256 tokenId, bool useAsCollateral, address sender ) external virtual override onlyPool nonReentrant returns (bool) {
The nonReentrant
modifier is similarly mispositioned in the following functions:
MintableIncentivizedERC721.sol: L474-484
MintableIncentivizedERC721.sol: L529-535
MintableIncentivizedERC721.sol: L540-546
NTokenApeStaking.sol: L102-106
NTokenApeStaking.sol: L159-163
No. | Description |
---|---|
3 | Missing NatSpec for some constructors |
NatSpec (consisting of an @notice or an @dev statement, as well as @param statements for any variables) is provided for most of the constructors in the contest. Below are instances of constructors with partially or completely missing NatSpec, which should be added to provide information and for consistency: |
UniswapV3OracleWrapper.sol: L23-27
constructor( address _factory, address _manager, address _addressProvider ) {
Missing: All NatSpec
DefaultReserveAuctionStrategy.sol: L50-57
constructor( uint256 maxPriceMultiplier, uint256 minExpPriceMultiplier, uint256 minPriceMultiplier, uint256 stepLinear, uint256 stepExp, uint256 tickLength ) {
Missing: All NatSpec
MintableIncentivizedERC721.sol: L77-88
/** * @dev Constructor. * @param pool The reference to the main Pool contract * @param name_ The name of the token * @param symbol_ The symbol of the token */ constructor( IPool pool, string memory name_, string memory symbol_, bool atomic_pricing ) {
Missing: @param atomic_pricing
/** * @dev Constructor. * @param pool The address of the Pool contract */ constructor(IPool pool, bool atomic_pricing) MintableIncentivizedERC721( pool, "NTOKEN_IMPL", "NTOKEN_IMPL", atomic_pricing )
Missing: @param atomic_pricing
/** * @dev Constructor. * @param pool The address of the Pool contract */ constructor(IPool pool, address apeCoinStaking) NToken(pool, false) { _apeCoinStaking = ApeCoinStaking(apeCoinStaking); }
Missing: @param apeCoinStaking
/** * @title BAYC NToken * * @notice Implementation of the NToken for the ParaSpace protocol */ contract NTokenBAYC is NTokenApeStaking { constructor(IPool pool, address apeCoinStaking) NTokenApeStaking(pool, apeCoinStaking)
Missing: @param
statements
/** * @title MAYC NToken * * @notice Implementation of the NToken for the ParaSpace protocol */ contract NTokenMAYC is NTokenApeStaking { constructor(IPool pool, address apeCoinStaking) NTokenApeStaking(pool, apeCoinStaking)
Missing: @param
statements
No. | Description |
---|---|
4 | Missing NatSpec for event |
Explanations (often consisting of an @notice or an @dev statement) are provided for almost all of the events in the contest. Below are two instances of events without such NatSpec: |
event ReserveUsedAsCollateralEnabled( address indexed reserve, address indexed user ); event ReserveUsedAsCollateralDisabled( address indexed reserve, address indexed user );
No. | Description |
---|---|
5 | Partially missing NatSpec for some functions |
NatSpec is partially missing for the following external or public functions . Note that, while it may be informative, NatSpec is not required for internal or private functions . I'm also ignoring functions preceded by references such as @inheritdoc IPriceOracleGetter |
/// @notice Allows owner to update oracle configs function setConfig(uint128 expirationPeriod, uint128 maxPriceDeviation) external onlyRole(DEFAULT_ADMIN_ROLE)
Missing: @param expirationPeriod
and @param maxPriceDeviation
/// @notice Allows owner to pause asset function setPause(address _asset, bool _flag) external onlyRole(DEFAULT_ADMIN_ROLE)
Missing: @param _asset
and @param _flag
/// @notice Allows owner to set new price on PriceInformation and updates the /// internal Median cumulativePrice. /// @param _assets The nft contract to set a floor price for function setMultiplePrices( address[] calldata _assets, uint256[] calldata _twaps ) external onlyRole(UPDATER_ROLE) {
Missing: @param _twaps
UniswapV3OracleWrapper.sol: L48-54
/** * @notice get onchain position data from uniswap for the specified tokenId. */ function getOnchainPositionData(uint256 tokenId) public view returns (UinswapV3PositionData memory)
Missing: @param tokenId
and @return
UniswapV3OracleWrapper.sol: L93-99
/** * @notice get onchain liquidity amount for the specified tokenId. */ function getLiquidityAmount(uint256 tokenId) external view returns (uint256 token0Amount, uint256 token1Amount)
Missing: @param tokenId
and two @return
statements
UniswapV3OracleWrapper.sol: L109-115
/** * @notice calculate liquidity amount for the position data. * @param positionData The specified position data */ function getLiquidityAmountFromPositionData( UinswapV3PositionData memory positionData ) public pure returns (uint256 token0Amount, uint256 token1Amount) {
Missing: Two @return
statements
UniswapV3OracleWrapper.sol: L124-130
/** * @notice get liquidity provider fee amount for the specified tokenId. */ function getLpFeeAmount(uint256 tokenId) external view returns (uint256 token0Amount, uint256 token1Amount)
Missing: @param tokenId
and two @return
statements
UniswapV3OracleWrapper.sol: L140-146
/** * @notice calculate liquidity provider fee amount for the position data. * @param positionData The specified position data */ function getLpFeeAmountFromPositionData( UinswapV3PositionData memory positionData ) public view returns (uint256 token0Amount, uint256 token1Amount) {
Missing: Two @return
statements
UniswapV3OracleWrapper.sol: L153-156
/** * @notice Returns the price for the specified tokenId. */ function getTokenPrice(uint256 tokenId) public view returns (uint256) {
Missing: @param tokenId
and @return
UniswapV3OracleWrapper.sol: L183-189
/** * @notice Returns the price for the specified tokenId array. */ function getTokensPrices(uint256[] calldata tokenIds) external view returns (uint256[] memory)
Missing: @param tokenIds
and @return
UniswapV3OracleWrapper.sol: L200-206
/** * @notice Returns the total price for the specified tokenId array. */ function getTokensPricesSum(uint256[] calldata tokenIds) external view returns (uint256)
Missing: @param tokenIds
and @return
/** * @notice Returns the user account data across all the reserves * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed * @return totalDebtBase The total debt of the user in the base currency used by the price feed * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed * @return currentLiquidationThreshold The liquidation threshold of the user * @return ltv The loan to value of The user * @return healthFactor The current health factor of the user * @return erc721HealthFactor The current erc721 health factor of the user **/ function executeGetUserAccountData( address user, DataTypes.PoolStorage storage ps, address oracle ) external view returns ( uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor, uint256 erc721HealthFactor )
Missing: @param user
, @param ps
and @param oracle
/** * @notice Returns the address of BAKC contract address. **/ function getBAKC() public view returns (IERC721) { return _apeCoinStaking.nftContracts(ApeStakingLogic.BAKC_POOL_ID);
Missing: @return
/** * @notice Returns the address of ApeCoinStaking contract address. **/ function getApeStaking() external view returns (ApeCoinStaking) { return _apeCoinStaking;
Missing: @return
/** * @notice Overrides the burn from NToken to withdraw all staked and pending rewards before burning the NToken on liquidation/withdraw */ function burn( address from, address receiverOfUnderlying, uint256[] calldata tokenIds ) external virtual override onlyPool nonReentrant returns (uint64, uint64) {
Missing: @param from
, @param receiverOfUnderlying
, @param tokenIds
and two @return
statements
NTokenApeStaking.sol: L178-185
/** * @notice get user total ape staking position * @param user user address */ function getUserApeStakingAmount(address user) external view returns (uint256)
Missing: @return
/** @dev an additional function that is custom to MoonBirds reserve. This function allows NToken holders to toggle on/off the nesting the status for the underlying tokens */ function toggleNesting(uint256[] calldata tokenIds) external {
Missing: @param tokenIds
/** @dev an additional function that is custom to MoonBirds reserve. This function allows NToken holders to get nesting the state for the underlying tokens */ function nestingPeriod(uint256 tokenId) external view returns ( bool nesting, uint256 current, uint256 total )
Missing: @param tokenId
and three @return
statements
/** @dev an additional function that is custom to MoonBirds reserve. This function check if nesting is open for the underlying tokens */ function nestingOpen() external returns (bool) { return IMoonBird(_underlyingAsset).nestingOpen();
Missing: @return
/** * @notice get user total ape staking position * @param userState The user state of nToken * @param ownedTokens The ownership mapping state of nNtoken * @param user User address * @param poolId identify whether BAYC pool or MAYC pool * @param _apeCoinStaking ApeCoinStaking contract address */ function getUserTotalStakingAmount( mapping(address => UserState) storage userState, mapping(address => mapping(uint256 => uint256)) storage ownedTokens, address user, uint256 poolId, ApeCoinStaking _apeCoinStaking ) external view returns (uint256) {
Missing: @return
/** * @notice get ape staking position for a tokenId * @param poolId identify whether BAYC pool or MAYC pool * @param _apeCoinStaking ApeCoinStaking contract address * @param tokenId specified the tokenId for the position */ function getTokenIdStakingAmount( uint256 poolId, ApeCoinStaking _apeCoinStaking, uint256 tokenId ) public view returns (uint256) {
Missing: @return
/** * @notice Implements the acceptBidWithCredit feature. AcceptBidWithCredit allows users to * accept a leveraged bid on ParaSpace NFT marketplace. Users can submit leveraged bid and pay * at most (1 - LTV) * $NFT * @dev The nft receiver just needs to do the downpayment * @param marketplaceId The marketplace identifier * @param payload The encoded parameters to be passed to marketplace contract (selector eliminated) * @param credit The credit that user would like to use for this purchase * @param referralCode The referral code used */ function acceptBidWithCredit( bytes32 marketplaceId, bytes calldata payload, DataTypes.Credit calldata credit, uint256[] calldata punkIndexes, uint16 referralCode ) external nonReentrant {
Missing: @param punkIndexes
/** * @notice Implements the batchAcceptBidWithCredit feature. AcceptBidWithCredit allows users to * accept a leveraged bid on ParaSpace NFT marketplace. Users can submit leveraged bid and pay * at most (1 - LTV) * $NFT * @dev The nft receiver just needs to do the downpayment * @param marketplaceIds The marketplace identifiers * @param payloads The encoded parameters to be passed to marketplace contract (selector eliminated) * @param credits The credits that the makers have approved to use for this purchase * @param referralCode The referral code used */ function batchAcceptBidWithCredit( bytes32[] calldata marketplaceIds, bytes[] calldata payloads, DataTypes.Credit[] calldata credits, uint256[] calldata punkIndexes, uint16 referralCode ) external nonReentrant {
Missing: @param punkIndexes
/** * @dev Get WPunk address used by WPunkGateway */ function getWPunkAddress() external view returns (address) {
Missing: @return
No. | Description |
---|---|
6 | Completely missing NatSpec for some functions |
NatSpec is completely or almost completely missing for the following external or public functions : |
Example:
function getBidOrderInfo( bytes memory /*params*/ ) external pure override returns (DataTypes.OrderInfo memory) {
Similarly for the following functions
:
LooksRareAdapter.sol
SeaportAdapter.sol
L25-29, L60-64, L93-97, L109-112
X2Y2Adapter.sol
L31-35, L79-83, L88-92, L104-108
NFTFloorOracle.sol
ParaSpaceOracle.sol
UniswapV3OracleWrapper.sol
MarketplaceLogic.sol
L63-70, L160-167, L229-237, L267-275
PoolLogic.sol
SupplyLogic.sol
DefaultReserveAuctionStrategy.sol
L66-67, L70-71, L74-75, L78-79
PoolCore.sol
PoolParameters.sol
MintableIncentivizedERC721.sol
NToken.sol
L52-59, L127-131, L136-140, L151-157
L168-171, L271-276, L280-286, L295-301
NTokenApeStaking.sol
NTokenBAYC.sol
NTokenMAYC.sol L113
NTokenMoonBirds.sol
NTokenUniswapV3.sol
MintableERC721Logic.sol
L80-87, L135-142, L187-197, L260-270, L340-344
L357-362, L368-372, L386-390, L424-428
WPunkGateway.sol
No. | Description |
---|---|
7 | Long single-line comments |
In theory, comments over 80 characters should wrap using multi-line comment syntax. Even if longer comments (up to 120 characters) are becoming | |
acceptable and a scroll bar is provided, very long comments can interfere with readability. Most of the long comments in the contest do wrap. | |
However, below are instances of extra-long comments (over 120 characters) whose readability could be improved | |
by wrapping, as shown. Note that a small indentation is used in each continuation line (which flags for the reader | |
that the comment is ongoing), along with a period at the close (to signal the end of the comment). In addition, | |
when an extra-long line includes both code and comments, it makes sense to put the comment in the line above. |
* @return The actual debt that is getting liquidated. If liquidation amount passed in by the liquidator is greater then the total user debt, then use the user total debt as the actual debt getting liquidated. If the user total debt is greater than the liquidation amount getting passed in by the liquidator, then use the liquidation amount the user is passing in.
Suggestion:
* @return The actual debt that is getting liquidated. If liquidation amount passed in by the * liquidator is greater than the total user debt, then use the user total debt as the actual * debt getting liquidated. If the user total debt is greater than the liquidation amount getting * passed in by the liquidator, then use the liquidation amount the user is passing in.
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
Suggestion:
// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f
* @notice Function to end auction on an ERC721 of a position if its ERC721 Health Factor increases back to above `AUCTION_RECOVERY_HEALTH_FACTOR`.
Suggestion:
* @notice Function to end auction on an ERC721 of a position if its ERC721 * Health Factor increases back to above `AUCTION_RECOVERY_HEALTH_FACTOR`.
* @notice Overrides the burn from NToken to withdraw all staked and pending rewards before burning the NToken on liquidation/withdraw
Suggestion:
* @notice Overrides the burn from NToken to withdraw all staked and * pending rewards before burning the NToken on liquidation/withdraw.
erc721Data.ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
Suggestion:
// Move the last token to the slot of the to-delete token erc721Data.ownedTokens[from][tokenIndex] = lastTokenId;
Similarly for the following extra-long (over 120 characters) comments:
PoolAddressesProvider.sol: L259
PoolAddressesProvider.sol: L338
No. | Description |
---|---|
8 | Typos |
* @notice undate incentive percentage for unstakePositionAndRepay
Change undate
to update
if that was intended
* @notice Function to tsatr auction on an ERC721 of a position if its ERC721 Health Factor drops below 1.
Change tsatr
to start
if that was intended
* @return The actual debt that is getting liquidated. If liquidation amount passed in by the liquidator is greater then the total user debt, then use the user total debt as the actual debt getting liquidated. If the user total debt is greater than the liquidation amount getting passed in by the liquidator, then use the liquidation amount the user is passing in.
Change the first then
to than
// eg. The following example image that the `taker` owns only ETH and wants to
Change image
to imagines
if that was intended
//we do not accept price lags behind to much
Change to
to too
The same typo (aggeregate
) occurs in all three lines referenced below:
/// aggeregate prices which are not expired from different feeders, if number of valid/unexpired prices
Change aggeregate
to aggregate
PoolAddressesProvider.sol: L336
* @notice Returns the the implementation contract of the proxy contract by its identifier.
Remove repeated word the
No. | Description |
---|---|
9 | Unclear comment |
Comments should communicate clearly, immediately and without ambiguity. The readability of the comment below could be improved: |
* @notice Function to tsatr auction on an ERC721 of a position if its ERC721 Health Factor drops below 1.
This @notice
is challenging to understand even after correcting tsatr
to start
No. | Description |
---|---|
10 | Inconsistent comment // spacing |
Some lines use //x (///x) whereas others use // x (/// x). While the choice of spacing is up to the developers, it should be made consistent (normally // x or /// x is used). |
//we need to deploy 3 oracles at least uint8 constant MIN_ORACLES_NUM = 3; //expirationPeriod at least the interval of client to feed data(currently 6h=21600s/12=1800 in mainnet) //we do not accept price lags behind to much uint128 constant EXPIRATION_PERIOD = 1800; //reject when price increase/decrease 1.5 times more than original value uint128 constant MAX_DEVIATION_RATE = 150; struct OracleConfig { // Expiration Period for each feed price uint128 expirationPeriod; // Maximum deviation allowed between two consecutive oracle prices uint128 maxPriceDeviation; }
No. | Description |
---|---|
11 | Solidity version should be upgraded |
The current solidity version in contracts is 0.8.10 , compared to the latest version of 0.8.17 . Only the latest version receives security fixes. | |
On the other hand, the latest version often has bugs, so it's safer to use releases a few versions older at first, as has been done here. | |
Having said that, version 0.8.12 or later is needed for string.concat to be used instead of abi.encodePacked and 0.8.13 is required to get the ability to use using for with a list of free functions |
No. | Description |
---|---|
12 | TODOs and other open items |
Comments that refer to open items should be addressed and modified or removed |
UniswapV3OracleWrapper.sol: L238
// TODO using bit shifting for the 2^96
// TODO: support PToken
{ // Intentionally left blank }
constructor(IPool pool) NToken(pool, false) { // Intentionally left blank }
#0 - c4-judge
2023-01-25T16:39:07Z
dmvt marked the issue as grade-a