xanoutas/solidity-security-findings huggingface dataset:
repayAndSupply might not supply back ApeCoin as collateral causing protocol fund loss
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/pool/PoolApeStaking.sol#L399
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/libraries/ApeStakingLogic.sol#L187


# Vulnerability details

## Impact
When ApeCoin gets unstaked using the functionality `unstakeAndRepay`, the remaining ApeCoin gets supplied back to the protocol within the call `repayAndSupply`. The supply call does not guarantee that the supplied ApeCoin will be marked as collateral and it can be attacked and cause the protocol to be drained of funds.

## Proof of Concept
1- User Supplies BAYC and stakes 1000 APEs with it
2- User supplies 1 APE and turn the collateralization off
3- BAYC gets liquidated and Unstake and Repay gets called which will end up supplying the APE back to the system but without being collateralized. casuing the protocol to be at a loss.

## Tools Used
Manual

## Recommended Mitigation Steps

Turn the APE as a collateral when calling `repayAndSupply`:

    function repayAndSupply(
        address underlyingAsset,
        address repayAsset,
        address onBehalfOf,
        uint256 repayAmount,
        uint256 supplyAmount
    ) external {
        DataTypes.PoolStorage storage ps = poolStorage();

        require(
            msg.sender == ps._reserves[underlyingAsset].xTokenAddress,
            Errors.CALLER_NOT_XTOKEN
        );

        if (repayAmount > 0) {
            BorrowLogic.executeRepay(
                ps._reserves,
                ps._usersConfig[onBehalfOf],
                DataTypes.ExecuteRepayParams({
                    asset: repayAsset,
                    amount: repayAmount,
                    onBehalfOf: onBehalfOf,
                    usePTokens: false
                })
            );
        }

        if (supplyAmount > 0) {
            ps._usersConfig[onBehalfOf].setUsingAsCollateral(ps._reserves[repayAsset].id, true);
            emit ReserveUsedAsCollateralEnabled(repayAsset, user);


            SupplyLogic.executeSupply(
                ps._reserves,
                ps._usersConfig[onBehalfOf],
                DataTypes.ExecuteSupplyParams({
                    asset: repayAsset,
                    amount: supplyAmount,
                    onBehalfOf: onBehalfOf,
                    payer: msg.sender,
                    referralCode: 0
                })
            );
        }
    }
No access control for the initialize function 
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/pool/PoolConfigurator.sol#L76


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
There is no access control for the ``initialize()`` function in ``PoolConfigurator.sol``. Also there is no zero address check for ``provider``. An attacker can front-run the ``initialize()`` function and set the wrong ``provider``.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/pool/PoolConfigurator.sol#L76

## Tools Used
Remix

## Recommended Mitigation Steps
Add a modifier as an access control, perform a zero address check, or use a factory pattern to deploy the contract so that the construction of the contract and the initialization can be done in one transaction to avoid frontrunning attack.
An attacker might frontrun the initialize function and set wrong amdin, _feeders, and _assets
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L97


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
There is no access control for the ``initialize`` function, so an attacker might frontrun this transaction and set the wrong admin, _feeders and _assets. 

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L97

## Tools Used
Remix
## Recommended Mitigation Steps
Add a modifier as an access control or use a factory pattern to deploy this contract so that the creation and intialization of the contract can be performed in one transaction to avoid frontrunning.
Upgraded Q -> M from #404 [1674736828553]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #404 as M risk. The relevant finding follows:

 ### [L‑06] Owner can renounce while system is paused

The contract owner or single user with a role is not prevented from renouncing the role/ownership while the contract is paused, which would cause any user assets stored in the protocol, to be locked indefinitely

_There is 1 instance of this issue:_

```solidity
File: paraspace-core/contracts/misc/NFTFloorOracle.sol

183       function setPause(address _asset, bool _flag)
184           external
185           onlyRole(DEFAULT_ADMIN_ROLE)
186       {
187           assetFeederMap[_asset].paused = _flag;
188           emit AssetPaused(_asset, _flag);
189:      }
```

[https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L183-L189](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L183-L189)

### [L‑11] Centralization risks

The admin can set whatever price they wish, causing anyone with NFT collateral to be liquidatable. The admin can also set a WETH address that just steals the funds. These operations should have more checks for market conditions before being allowed.

```solidity
File: /paraspace-core/contracts/misc/NFTFloorOracle.sol   #1

195      function setPrice(address _asset, uint256 _twap)
196          public
197          onlyRole(UPDATER_ROLE)
198          onlyWhenAssetExisted(_asset)
199          whenNotPaused(_asset)
200      {
201          bool dataValidity = false;
202          if (hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
203              _finalizePrice(_asset, _twap);
204              return;
205:         }
```

[https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L195-L205](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L195-L205)

```solidity
File: /paraspace-core/contracts/protocol/configuration/PoolAddressesProvider.sol   #2

235      function setWETH(address newWETH) external override onlyOwner {
236          address oldWETH = _addresses[WETH];
237          _addresses[WETH] = newWETH;
238          emit WETHUpdated(oldWETH, newWETH);
239:     }
```

[https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/configuration/PoolAddressesProvider.sol#L235-L239](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/configuration/PoolAddressesProvider.sol#L235-L239)
Upgraded Q -> M from #404 [1674736594739]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #404 as M risk. The relevant finding follows:

 [L‑04] latestAnswer() is deprecated
Use latestRoundData() instead so that you can tell whether the answer is stale or not. The latestAnswer() function returns zero if it is unable to fetch data, which may be the case if ChainLink stops supporting this API. The API and its deprecation message no longer even appear on the ChainLink website, so it is dangerous to continue using it.

There is 1 instance of this issue:

File: paraspace-core/contracts/misc/ParaSpaceOracle.sol

128:              price = uint256(source.latestAnswer());
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/ParaSpaceOracle.sol#L128
Upgraded Q -> M from #472 [1674665995647]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #472 as M risk. The relevant finding follows:

 [L-01] CHAINLINK AGGREGATOR IS NOT SUFFICIENTLY VALIDATED AND CAN RETURN STALE ANSWER
As shown below, calling the getAssetPrice function in the ParaSpaceOracle contract can execute price = uint256(source.latestAnswer()), where source is a Chainlink aggregator. If the answer returned by the Chainlink aggregator is 0, a fallback oracle can then be used for setting price. However, besides that Chainlink's latestAnswer function is deprecated, only verifying if the returned answer is positive is also not enough to guarantee that the returned answer is not stale. Using a stale answer as price can have unexpected consequences, such as mistakenly considering a user position unhealthy that allows liquidation.

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/ParaSpaceOracle.sol#L115-L136

    function getAssetPrice(address asset)
        public
        view
        override
        returns (uint256)
    {
        if (asset == BASE_CURRENCY) {
            return BASE_CURRENCY_UNIT;
        }

        uint256 price = 0;
        IEACAggregatorProxy source = IEACAggregatorProxy(assetsSources[asset]);
        if (address(source) != address(0)) {
            price = uint256(source.latestAnswer());
        }
        if (price == 0 && address(_fallbackOracle) != address(0)) {
            price = _fallbackOracle.getAssetPrice(asset);
        }

        require(price != 0, Errors.ORACLE_PRICE_NOT_READY);
        return price;
    }
To avoid using a stale answer returned by the Chainlink aggregator, according to Chainlink's documentation:

The latestRoundData function can be used instead of the deprecated latestAnswer function.
roundId and answeredInRound are also returned. "You can check answeredInRound against the current roundId. If answeredInRound is less than roundId, the answer is being carried over. If answeredInRound is equal to roundId, then the answer is fresh."
"A read can revert if the caller is requesting the details of a round that was invalid or has not yet been answered. If you are deriving a round ID without having observed it before, the round might not be complete. To check the round, validate that the timestamp on that round is not 0."
Hence, as a mitigation, https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/ParaSpaceOracle.sol#L127-L129 can be updated to the following code.

        if (address(source) != address(0)) {
            (uint80 roundId, int256 answer, , uint256 updatedAt, uint80 answeredInRound) = source.latestRoundData();
            require(answeredInRound >= roundId, "answer is stale");
            require(updatedAt > 0, "round is incomplete");
            require(answer > 0, "Invalid answer");

            price = uint256(answer);            
        }
Upgraded Q -> M from #449 [1674665297296]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #449 as M risk. The relevant finding follows:

 [L‑03] MintableIncentivizedERC721 does not implement ERC721.safeTransferFrom properly
MintableIncentivizedERC721 is described as:

27:  * @notice Basic ERC721 implementation
which will be used as a parent contract for NTokens.

It however does not implement safeTransferFrom as per EIP-721. This function should:

When transfer is complete, this function
///  checks if `_to` is a smart contract (code size > 0). If so, it calls
///  `onERC721Received` on `_to` and throws if the return value is not
///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
But looking at the implementation, it simply performs the _transfer, without performing these checks:

320: function _safeTransfer(
321:         address from,
322:         address to,
323:         uint256 tokenId,
324:         bytes memory
325:     ) internal virtual {
326:         _transfer(from, to, tokenId);
327:     }
NToken, which inherits MintableIncentivizedERC721, does not override this function. Neither do all the NToken implementations.

Recommendation
Add the required check to MintableIncentivizedERC721._safeTransfer.

320: function _safeTransfer(
321:         address from,
322:         address to,
323:         uint256 tokenId,
324:         bytes memory
325:     ) internal virtual {
326:         _transfer(from, to, tokenId);
+            require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
327:     }
Upgraded Q -> M from #374 [1674664049404]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #374 as M risk. The relevant finding follows:

 [L-03] WETHGateway.repayETH will revert if msg.value > paybackAmount
In the repayETH function, paybackAmount eth will be deposit to WETH contract to get paybackAmount weth back.

WETH.deposit{value: paybackAmount}();
And then repay msg.value to pool

IPool(pool).repay(address(WETH), msg.value, onBehalfOf);
Here if msg.value > paybackAmount, IPool(pool).repay will revert due to insufficient weth.

Recommendation
change

IPool(pool).repay(address(WETH), msg.value, onBehalfOf);
to

IPool(pool).repay(address(WETH), paybackAmount, onBehalfOf);
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/ui/WETHGateway.sol#L112-L113
Upgraded Q -> M from #313 [1674663275698]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #313 as M risk. The relevant finding follows:

 [L-07] centralization risks
The owner address of PoolAddressesProvider contract has control over many dangerous functions. Such as:

setAddressAsProxy, setPoolConfiguratorImpl and updatePoolImpl which are responsable for setting the pool diamon proxy implementation

setPriceOracle setPriceOracleSentinel: for settings the used oracle for the protocol prices

setACLAdmin: for setting the access control administration

All mentioned functions should have a time lock for the users to have more confidence in the protocol.
Upgraded Q -> M from #258 [1674661917738]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #258 as M risk. The relevant finding follows:

 [L-03] Use of deprecated functions
Impact
The contract uses deprecated function latestAnswer(). Such functions might suddenly stop working if no longer supported.
Impact: Deprecated API stops working. Prices cannot be obtained. Protocol stops and contracts have to be redeployed.

Findings:
contracts/misc/ParaSpaceOracle.sol:L128            price = uint256(source.latestAnswer());
Upgraded Q -> M from #229 [1674661441196]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #229 as M risk. The relevant finding follows:

 Support for IERC165 interface id is missed
Contract:
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/base/MintableIncentivizedERC721.sol#L572

Impact:
Contract fails to support a valid interface which could lead to failure of genuine calls

Steps:

Observe the supportsInterface
function supportsInterface(bytes4 interfaceId)
        external
        view
        virtual
        override(IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IERC721Enumerable).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId;
    }
Observe that support for IERC165 interface id is missing
Recommendation:
Kindly revise the function as below:

function supportsInterface(bytes4 interfaceId)
        external
        view
        virtual
        override(IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IERC721Enumerable).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
Upgraded Q -> M from #229 [1674661320954]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #229 as M risk. The relevant finding follows:

 Centralization Risk
Contract:
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/base/MintableIncentivizedERC721.sol#L131

Impact:
It seems the poolAdmin holds too much power including changing reward controller, rescue tokens etc. This can allow poolAdmin to impact all users by changing the config or draining the contract. In this example we will see one example for setIncentivesController

Steps:

PoolAdmin calls setIncentivesController and set rewardController to zero
This causes Users will stop getting incentives on their stakes. So if User decides to burn then the reward incentives are gone permanently
Recommendation:
Keep the poolAdmin as multiSig and behind timelock to prevent immediate changes
Upgraded Q -> M from #80 [1674644981726]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #80 as M risk. The relevant finding follows:

 [LOW‑10] getPrice and combine will not work if expirationPeriod == 0
The following conditions will fail if expirationPeriod is set to 0. There is currently no limit that it cannot be set to 0.

Proof Of Concept
243: require(
244:    (block.number - updatedAt) <= config.expirationPeriod,
245:    "NFTOracle: asset price expired"
246: );
https://github.com/code-423n4/2022-11-paraspace/tree/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L243-L246

419: if (diffBlock <= config.expirationPeriod) {
420:    validPriceList[validNum] = priceInfo.twap;
421:    validNum++;
422: }
https://github.com/code-423n4/2022-11-paraspace/tree/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L419-L422
Upgraded Q -> M from #72 [1674644492627]
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

Judge has assessed an item in Issue #72 as M risk. The relevant finding follows:

 [Low-03] NTokenMoonBirds may not be able to receive airdrops
Impact
For most NToken, some airdrops that are actively minted to the holder's address can be withdrawn and later distributed by the PoolAdmin calling the rescueERC721 function.

    function rescueERC721(
        address token,
        address to,
        uint256[] calldata ids
    ) external override onlyPoolAdmin {
        require(
            token != _underlyingAsset,
            Errors.UNDERLYING_ASSET_CAN_NOT_BE_TRANSFERRED
        );
        for (uint256 i = 0; i < ids.length; i++) {
            IERC721(token).safeTransferFrom(address(this), to, ids[i]);
        }
        emit RescueERC721(token, to, ids);
    }
However, in the onERC721Received function of the NTokenMoonBirds contract, due to the requirement that the sender can only be the MoonBird contract, when safemint()/safetransferfrom() is called to send the airdrop NFTs to the NTokenMoonBirds contract, the transaction will fail, thus preventing NTokenMoonBirds from receiving these airdrops.

    function onERC721Received(
        address operator,
        address from,
        uint256 id,
        bytes memory
    ) external virtual override returns (bytes4) {
        // only accept MoonBird tokens
        require(msg.sender == _underlyingAsset, Errors.OPERATION_NOT_SUPPORTED);
For example, Moonbirds Oddities are actively minted to the holder's address.
https://etherscan.io/tx/0x3af5de8b6a8c55aac033d57e1b110e8340abf4dcd289ebda889a44f9f9dc613d

Proof of Concept
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/tokenization/NToken.sol#L136-L149
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/tokenization/NTokenMoonBirds.sol#L63-L77

Recommended Mitigation Steps
Consider allowing the NTokenMoonBirds contract to receive NFTs from other addresses and only call POOL.supportERC721FromNToken when msg.sender == _underlyingAsset

    function onERC721Received(
        address operator,
        address from,
        uint256 id,
        bytes memory
    ) external virtual override returns (bytes4) {
        // only accept MoonBird tokens
-        require(msg.sender == _underlyingAsset, Errors.OPERATION_NOT_SUPPORTED);

        // if the operator is the pool, this means that the pool is transferring the token to this contract
        // which can happen during a normal supplyERC721 pool tx
        if (operator == address(POOL)) {
            return this.onERC721Received.selector;
        }
+     if(msg.sender == _underlyingAsset){       
        // supply the received token to the pool and set it as collateral
        DataTypes.ERC721SupplyParams[]
            memory tokenData = new DataTypes.ERC721SupplyParams[](1);

        tokenData[0] = DataTypes.ERC721SupplyParams({
            tokenId: id,
Missing ReEntrancy Guard to  `executeAcceptBidWithCredit`  function
Audit: 2022-11-paraspace-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L229-L265


# Vulnerability details

### Impact

if the mint was initiated by a contract, then the contract is checked for its ability to receive ERC721 tokens. Without reentrancy guard, onERC721Received will allow an attacker controlled contract to call the mint again, which may not be desirable to some parties, like allowing minting more than allowed.
https://www.paradigm.xyz/2021/08/the-dangers-of-surprising-code



`executeAcceptBidWithCredit` has internal function `_acceptBidWithCredit` ;

[MarketplaceLogic.sol#L247](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L247)


`_acceptBidWithCredit` has internal function `_repay` ;

[MarketplaceLogic.sol#L353](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L353)

`_repay ` has internal function `_transferAndCollateralize` ;


[MarketplaceLogic.sol#L503](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L503)


`_transferAndCollateralize`  function use `IERC721.safeTransferFrom` and this function has re-entrancy vulnerability


[MarketplaceLogic.sol#L593](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/libraries/logic/MarketplaceLogic.sol#L593)

```solidity
 function _transferAndCollateralize(
        mapping(address => DataTypes.ReserveData) storage reservesData,
        DataTypes.UserConfigurationMap storage userConfig,
        MarketplaceLocalVars memory vars,
        address token,
        uint256 tokenId,
        address onBehalfOf
    ) internal {
        uint256[] memory tokenIds = new uint256[](1);
        tokenIds[0] = tokenId;

        IERC721(vars.xTokenAddress).safeTransferFrom(
            address(this),
            onBehalfOf,
            tokenId
        );
        SupplyLogic.executeCollateralizeERC721(
            reservesData,
            userConfig,
            token,
            tokenIds,
            onBehalfOf
        );
    }

```




### Recommended Mitigation Steps

Use Openzeppelin or Solmate Re-Entrancy pattern
Here is a example of a re-entrancy guard;


```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract ReEntrancyGuard {
    bool internal locked;

    modifier noReentrant() {
        require(!locked, "No re-entrancy");
        locked = true;
        _;
        locked = false;
    }
}
```