xanoutas/solidity-security-findings huggingface dataset:
CryptoPunk can to be stolen by front-running WPunkGateway 's supplyPunk()
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/ui/WPunkGateway.sol#L83-L86
https://github.com/larvalabs/cryptopunks/blob/11532167fa705ced569fc3206df0484f9027e1ee/contracts/CryptoPunksMarket.sol#L156-L184
https://github.com/larvalabs/cryptopunks/blob/11532167fa705ced569fc3206df0484f9027e1ee/contracts/CryptoPunksMarket.sol#L148-L154
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/pool/PoolCore.sol#L113-L132
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/SupplyLogic.sol#L196-L200
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/SupplyLogic.sol#L134-L156


# Vulnerability details

## Impact
The WPunkGateway's [`supplyPunk()`](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/ui/WPunkGateway.sol#L83)) function is called by the user to supply their punk to ParaSpace. It uses the [`buyPunk()`](https://github.com/larvalabs/cryptopunks/blob/11532167fa705ced569fc3206df0484f9027e1ee/contracts/CryptoPunksMarket.sol#L156-L184) function on the cryptopunk contract to pull the punk into the ` WPunkGateway` contract. 
In order to do so the user first has to offer the punk for sale (for 0 ETH) to the `WPunkGateway` (proxy) contract. This is because the CyrptoPunk contract does not support the standard `approve/transferFrom` methods.

The `supplyPunk()` function does not however check whether the `msg.sender()` is in fact the owner of the cryptopunk and neither does the cyrptopunk contract itself. (It doesn't have to as it was offered for sale to the address for an agreed price, in this case 0 ETH) 
This means that once the Cryptopunk's [`offerPunkForSaleToAddress()`](https://github.com/larvalabs/cryptopunks/blob/11532167fa705ced569fc3206df0484f9027e1ee/contracts/CryptoPunksMarket.sol#L148-L154) is called by the owner, anyone (including an MEVbot) can call the `supplyPunk()` function.

```solidity
function supplyPunk(
	DataTypes.ERC721SupplyParams[] calldata punkIndexes,
	address onBehalfOf,
	uint16 referralCode
) external nonReentrant {
	for (uint256 i = 0; i < punkIndexes.length; i++) {
	Punk.buyPunk(punkIndexes[i].tokenId);
	Punk.transferPunk(proxy, punkIndexes[i].tokenId);
	// gatewayProxy is the sender of this function, not the original gateway
	WPunk.mint(punkIndexes[i].tokenId);
	}
	Pool.supplyERC721(
		address(WPunk),
		punkIndexes,
		onBehalfOf,
		referralCode
	);
}
```

When this happens the WPunkGateway contract will pull (`buyPunk()/transferPunk()`) the CryptoPunk and mint a wrapped punk making the WPunkGateway proxy contract the owner of the WPunk. 
The wrapped punk is then supplied to the pool via `Pool.supplyERC721(...)`. 
Note
Supply logic for Uniswap V3 collateral is flawed and can lead to platform over-collateralized by certain assets
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/ValidationLogic.sol#L1155-L1213


# Vulnerability details

## Impact

ERC20 supply cap can be surpassed using Uniswap V3 positions, which could lead to insolvency

## Proof of Concept

[ValidationLogic#validateForUniswapV3](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/ValidationLogic.sol#L1155-L1213) only checks that the underlying tokens of the Uniswap V3 NFT are allowed to be supplied and used as collateral but never checks the current supply of these tokens. Supply caps are built into the protocol to protect against insolvency resulting taking more loans than liquidity would allow to be disposed of (similar to what happened recently with FTX). Since the Uniswap V3 NFT directly represents the underlying tokens, the protocol is by extent using those underlying as collateral. With no checks in place the collateralization of the system can get completely out of balance which can result in the entire platform becoming insolvent.

Imagine the following scenario:

TokenX is used as collateral on the platform, with a supply cap of 1,000,000. Since it is a valid collateral it is automatically enabled as a valid token for UniswapV3 collateralization. Now imagine that TokenX is exploited and an attacker is able to mint an unlimited number of TokenX. With the supply cap, the damage is limited to the number of assets they can borrow with 1,000,000 TokenX. With the UniswapV3 collateral the damage has now increased to all other collateral in the platform. The attacker could add their token to the UniswapV3 pool single sided so that the entire NFT is only backed by TokenX. Now they can deposit their UniswapV3 NFT and borrow all the collateral on the entire platform.

## Tools Used

Manual Review

## Recommended Mitigation Steps

Check the number of underlying tokens for the UniswapV3 NFT and factor in the deposit amount against each tokens supply cap.
The attacker authorizes any tokenid to steal assets
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/libraries/MintableERC721Logic.sol#L340-L355
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/base/MintableIncentivizedERC721.sol#L191-L201
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/base/MintableIncentivizedERC721.sol#L253-L265


# Vulnerability details

## Impact
The attacker authorizes any tokenid to steal assets

## Proof of Concept
`MintableERC721Logic:;executeApprove()` does not have any permission verification

[2022-11-paraspace/MintableERC721Logic.sol at main · code-423n4/2022-11-paraspace · GitHub](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/libraries/MintableERC721Logic.sol#L340-L355)
```solidity
    function executeApprove( //@audit 
        MintableERC721Data storage erc721Data,
        address to,
        uint256 tokenId
    ) external {
        _approve(erc721Data, to, tokenId);
    }

    function _approve(
        MintableERC721Data storage erc721Data,
        address to,
        uint256 tokenId
    ) private {
        erc721Data.tokenApprovals[tokenId] = to;
        emit Approval(erc721Data.owners[tokenId], to, tokenId);
    }
```

The attacker can specify `erc721Data.tokenApprovals[tokenId]` corresponding to any tokenid as any address. For example, the attacker's address.
Thus bypassing the judgment of the `owner` by `approve()` in `mintableincentivizerc721`
[2022-11-paraspace/MintableIncentivizedERC721.sol at main · code-423n4/2022-11-paraspace · GitHub](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/base/MintableIncentivizedERC721.sol#L191-L201)
```solidity
    function approve(address to, uint256 tokenId) external virtual override {
        address owner = ownerOf(tokenId);
        require(to != owner, "ERC721: approval to old owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        MintableERC721Logic.executeApprove(_ERC721Data, to, tokenId);
    }
```
Once authorized, the attacker can transfer the victim's assets
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/base/MintableIncentivizedERC721.sol#L253-L265
```solidity
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external virtual override nonReentrant {
        //solhint-disable-next-line max-line-length
        require(
            _isApprovedOrOwner(_msgSender(), tokenId),
            "ERC721: transfer caller is not owner nor approved"
        );

        _transfer(from, to, tokenId);
    }
```
## Tools Used
vscode
## Recommended Mitigation Steps
Determine the owner of nft when
Call `executeUnstakePositionAndRepay()` directly to bypass the `onlyPool` check
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/libraries/ApeStakingLogic.sol#L92-L96
https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/NTokenApeStaking.sol#L161


# Vulnerability details

## Impact

Call `executeUnstakePositionAndRepay()` directly to bypass the `onlyPool` check


## Proof of Concept

`apeStakingLogic::executeUnstakePositionAndRepay()` can be called directly

 [2022-11-paraspace/ApeStakingLogic.sol at main · code-423n4/2022-11-paraspace · GitHub](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/libraries/ApeStakingLogic.sol#L92-L96)
```solidity
    function executeUnstakePositionAndRepay( //@audit 
        mapping(uint256 => address) storage _owners,
        APEStakingParameter storage stakingParameter,
        UnstakeAndRepayParams memory params
    ) external {
        address positionOwner = _owners[params.tokenId];
        IERC20 _apeCoin = params._apeCoinStaking.apeCoin();
        uint256 balanceBefore = _apeCoin.balanceOf(address(this));
```
 Thus bypassing the onlypool check in `NTokenApeStaking::unstakePositionAndRepay()`
[2022-11-paraspace/NTokenApeStaking.sol at main · code-423n4/2022-11-paraspace · GitHub](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/protocol/tokenization/NTokenApeStaking.sol#L161)
```solidity
    function unstakePositionAndRepay(uint256 tokenId, address incentiveReceiver)
        external
        onlyPool
        nonReentrant
    {
        ApeStakingLogic.executeUnstakePositionAndRepay(
            _ERC721Data.owners,
            apeStakingDataStorage(),
            ApeStakingLogic.UnstakeAndRepayParams({
                POOL: POOL,
                _apeCoinStaking: _apeCoinStaking,
                _underlyingAsset: _underlyingAsset,
                poolId: POOL_ID(),
                tokenId: tokenId,
                incentiveReceiver: incentiveReceiver
            })
        );
    }
```


## Tools Used
vscode 
## Recommended Mitigation Steps
Add authentication to the call to `executeUnstakePositionAndRepay()`
Uniswap v3 lp oracle decimal 
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

The `oracleData` calculation takes into account the difference between token0 and token1 decimals. But the current implementation is inconsistent. If the lp pair has 2 different decimal tokens, the returned price could be way off.

## Proof of Concept

In the case `token1Decimal > token0Decimal`, the denominator is `1E9`, but if `token1Decimal < token0Decimal`, the denominator becomes `10 ** (9 + oracleData.token0Decimal - oracleData.token1Decimal)`. The first treatment seems to be right, since the decimal difference is included in the nominator.

```solidity
File: paraspace-core/contracts/misc/UniswapV3OracleWrapper.sol
221:     function _getOracleData(UinswapV3PositionData memory positionData)
222:         internal
223:         view
224:         returns (PairOracleData memory)
225:     {

249:         } else if (oracleData.token1Decimal > oracleData.token0Decimal) {
250:             // multiple by 10^(decimalB - decimalA) to preserve price in wei
251:             oracleData.sqrtPriceX96 = uint160(
252:                 (SqrtLib.sqrt(
253:                     (oracleData.token0Price *
254:                         (10 **
255:                             (18 +
256:                                 oracleData.token1Decimal -
257:                                 oracleData.token0Decimal))) /
258:                         (oracleData.token1Price)
259:                 ) * 2**96) / 1E9
260:             );
261:         } else {
262:             // multiple by 10^(decimalA - decimalB) to preserve price in wei then divide by the same number
263:             oracleData.sqrtPriceX96 = uint160(
264:                 (SqrtLib.sqrt(
265:                     (oracleData.token0Price *
266:                         (10 **
267:                             (18 +
268:                                 oracleData.token0Decimal -
269:                                 oracleData.token1Decimal))) /
270:                         (oracleData.token1Price)
271:                 ) * 2**96) /
272:                     10 **
273:                         (9 +
274:                             oracleData.token0Decimal -
275:                             oracleData.token1Decimal)
276:             );
277:         }
```


## Tools Used
Manual analysis.

## Recommended Mitigation Steps

Change the last else block to:
```solidity
        } else {
            // multiple by 10^(decimalA - decimalB) to preserve price in wei then divide by the same number
            oracleData.sqrtPriceX96 = uint160(
                (SqrtLib.sqrt(
                    (oracleData.token0Price *
                        (10 **
                            (18 +
                                oracleData.token0Decimal -
                                oracleData.token1Decimal))) /
                        (ora
ApeCoin Supply Intended For ParaSpace's Ape Staking Feature Can Be Abused By Malicious Users
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/logic/FlashClaimLogic.sol#L28


# Vulnerability details

## Background

By supplying NFTs and ERC-20s in ParaSpace, users can borrow ERC-20 tokens via `PoolCore.borrow` function. The borrowed ERC-20 tokens will be sent to the user's wallet, and the users are free to use them for any purpose.

However, ParaSpace does not allow users to borrow Staked ApeCoin (sAPE) or ApeCoin (APE) from the pool directly and send them to their wallet address.

The following ParaSpace's test cases show that the protocol does not allow users to borrow sAPE or APE. Thus, one of the protocol invariants is that no one should be able to borrow sAPE or APE, and forward them to their wallet under any circumstances, even if their account is in good standing (Health Factor > 1). 

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/test/_sape_pool_operation.spec.ts#L89

```typescript
File: _sape_pool_operation.spec.ts
089:   it("borrow sApe is not allow", async () => {
090:     const {
091:       users: [user1],
092:       pool,
093:     } = testEnv;
094: 
095:     await expect(
096:       pool.connect(user1.signer).borrow(sApeAddress, 111, 0, user1.address, {
097:         gasLimit: 12_450_000,
098:       })
099:     ).to.be.revertedWith(ProtocolErrors.BORROWING_NOT_ENABLED);
100:   });
```

Currently, the protocol only allows users to borrow APE for staking purposes via the `PoolApeStaking.borrowApeAndStake`. When a user borrows APE from the pool, ParaSpace will instantaneously stake any borrowed APE along with the relevant BAYC/MAYC NFT to the $APE Staking Contract. There is no way for the users to pull or forward the borrowed APE to their wallet addresses.

In short, the borrowed sAPE or APE for staking purposes must not leave ParaSpace and the official $APE Staking Contract boundaries. Users can only claim the accrued APE rewards and forward them to their wallet addresses.

However, there is a vulnerability that allows users to bypass this invariant and allow users to forward the borrowed APE to their wallet address. 

## Proof of Concept

Assume that Mallory's (attacker) account is in very good standing (e.g. Health Factor > 100), and he has 0 APE in her wallet. Thus, he borrowed the maximum stake of 10,094 APE from ParaSpace. The borrowed 10,094 APE is instantaneously staked into the $APE Staking Contract.

Within a single transaction, Mallory calls the `PoolCore.flashClaim` function to claim his BAYC NFT and forward it to his malicious contract. The BAYC NFT provides access to the staked ApeCoin in the official $APE Staking Contract. Thus, the malicious contract that holds the the BAYC NFT could call the `ApeCoinStaking.withdrawApeCoin` function of the official $APE Staking Contract to withdraw 10,094 ApeCoin linked to his BAYC NFT and forward them to his wall
`WPunkGateway` Vulnerable To Frontrunning Attack That Result In Loss Of Punk NFT
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-11-paraspace/blob/c01a980e5d6e15b2993b912c3569ed8b5236ff33/paraspace-core/contracts/ui/WPunkGateway.sol#L129


# Vulnerability details

## Background

#### History Of Punk NFT

Punk NFT was developed many years back before ERC721 standard was established. Thus, they took inspiration from the ERC20 standard and removed the `transfer` and `transferFrom` functions to achieve the "Non-Fungible" characteristic of their NFT "token". 

Back then, there was also no `approve` or `setApprovalForAll` function since ERC721 standard had not been established yet. Thus, there is no way to grant allowance to the marketplace to spend or manage the punk NFTs on behalf of the users. As such, a workaround is needed, and the workaround is to use `Punk.offerPunkForSaleToAddress`, `Punk.offerPunkForSale,` and `Punk.buyPunk` to achieve the same result as allowance/approval in ERC721.

How it works is that the seller will call `offerPunkForSaleToAddress` to grant allowance to the marketplace/operator, and the marketplace/operator will call `buyPunk` to transfer the NFT from the seller.

This workaround is used within the `WPunkGateway.acceptBidWithCredit` function.

## Proof of Concept

Following is the specification of the `CryptoPunksMarket.offerPunkForSaleToAddress` function.

https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/mocks/tokens/CryptoPunksMarket.sol#L210

```solidity
File: CryptoPunksMarket.sol
210:     function offerPunkForSaleToAddress(
211:         uint256 punkIndex,
212:         uint256 minSalePriceInWei,
213:         address toAddress
214:     ) public {
```

The following gives an overview of how the `WPunkGateway.acceptBidWithCredit` function works under the hood. This is needed to understand the vulnerability.

1. Bob (maker) bids Alice's Punk NFT #2 for 1000 ETH. The 1000 ETH consists of 800 ETH payNowAmount and 200 ETH credit. In other words, Bob has to pay 800 ETH from his wallet and borrow 200 ETH from ParaSpace when the deal happens.
2. Suppose Alice (taker) wants to accept Bob's bid for her Punk NFT #2 (punkIndex=2). In that case, Alice will call the `CryptoPunksMarket.offerPunkForSaleToAddress(2, 0, Punk_Gateway_Address)` first.

3. Alice will call the `WPunkGateway.acceptBidWithCredit` function.  The Punk Gateway will obtain/buy Alice's Punk NFT #2 from her wallet. Since the `offer.minValue` is zero, the Punk Gateway does not need to send any ETH for the transaction. Note that only the Punk Gateway can buy Alice's Punk NFT #2 because the `offer.onlySellTo` has been explicitly set to the Punk Gateway. Thus, there is no risk that other users can obtain Alice's Punk NFT #2 for zero wei.

4. The Punk Gateway will send the newly obtained Punk NFT #2 to WPunk Proxy to mint the Wrapped Punk NFT #2. Newly minted wrapped Punk NFT #2 will be returned to the Punk Gateway.
5. The Punk Gateway will send the wrapped Punk NFT #2 back to
Missing `onlyRole` modifier
Audit: 2022-11-paraspace-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

According to [this comment](https://github.com/code-423n4/2022-11-paraspace/blob/main/paraspace-core/contracts/misc/NFTFloorOracle.sol#L165), `removeFeeder()` is intended to be called only by the owner to remove the feeder.

However, because it is missing the modifier `onlyRole()` and since `removeFeeder()` is also `external` anyone can make a call to `removeFeeder()` and remove its feeder.

The owner may try to add feeders back by calling [`addFeeders()`](https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/NFTFloorOracle.sol#L156-L163), but a malicious user can continuously call `removeFeeder()` to remove the feeder every time the owner adds the feeder back.

Depending on the price feed information and updates, this can disrupt the protocol on all function calls.

## Proof of Concept

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

```

/// @notice Allows owner to remove feeder.
/// @param _feeder feeder to remove
function removeFeeder(address _feeder)
    external
    onlyWhenFeederExisted(_feeder)
{
    _removeFeeder(_feeder);
}
```

As we can see, `removeFeeder()` does not have an `onlyRole()` modifier to verify if the person calling is the owner.

## Recommended Mitigation Steps

Add the modifier `onlyRole()` to the `removeFeeder()` function