HIGH severity: [FNG-8] All NFTs are evaluated at their floor price and can lead to user's loss. **Severity:** Medium

**Path:** CErc721.sol:doNFTTransferOut#L624-L637

**Description:** In the `CErc721.sol` when the user mints CTokens with the `mint()` function the user supplies `nftIds` which they would like to exchange for CTokens. But the `exchangeRate` is fixed for any NFT in that...

Vulnerable code:
function mint(uint[] memory nftIds) external override nonReentrant returns (uint) {

        comptroller.autoEnterMarkets(msg.sender); // silent failure allowed

        accrueInterest();

        uint mintAmount = nftIds.length * expScale;
        address minter = msg.sender;

        /* Fail if mint not allowed */
        uint allowed = comptroller.mintAllowed(address(this), minter, mintAmount);
        if (allowed != 0) {
            revert MintComptrollerRejection(allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            revert MintFreshnessCheck();
        }

        Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        supplyInterest[minter].interestAccrued = supplyInterestStoredInternal(minter);
        supplyInterest[minter].interestIndex = supplyIndex;

        uint actualMintAmount = doNFTTransferIn(minter, nftIds) * expScale;

        /*
         * We get the current exchange rate and calculate the number of cTokens to be minted:
         *  mintTokens = actualMintAmount / exchangeRate
         */

        uint mintTokens = div_(actualMintAmount, exchangeRate);

        /*
         * We calculate the new total supply of cTokens and minter token balance, checking for overflow:
         *  totalSupplyNew = totalSupply + mintTokens
         *  accountTokensNew = accountTokens[minter] + mintTokens
         * And write them into storage
         */
        totalSupply = totalSupply + mintTokens;
        accountTokens[minter] = accountTokens[minter] + mintTokens;

        /* We emit a Mint event, and a Transfer event */
        emit Mint(minter, actualMintAmount, mintTokens, nftIds);
        emit Transfer(address(this), minter, mintTokens);

        /* We call the defense hook */
        // unused function
        // comptroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);

        return NO_ERROR;
    }

function redeemFresh(address payable redeemer, uint redeemTokensIn, uint redeemAmountIn) internal override {
        require(redeemTokensIn == 0 || redeemAmountIn == 0, "one of redeemTokensIn or redeemAmountIn must be zero");

        // NFT count -> amount
        redeemAmountIn = redeemAmountIn * expScale; //1e18

        /* exchangeRate = invoke Exchange Rate Stored() */
        Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal() });

        uint redeemTokens;
        uint redeemAmount;
        /* If redeemTokensIn > 0: */
        if (redeemTokensIn > 0) {
            /*
             * We calculate the exchange rate and the amount of underlying to be redeemed:
             *  redeemTokens = redeemTokensIn
             *  redeemAmount = redeemTokensIn x exchangeRateCurrent
             */
            redeemTokens = redeemTokensIn;
            redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn);
        } else {
            /*
             * We get the current exchange rate and calculate the amount to be redeemed:
             *  redeemTokens = redeemAmountIn / exchangeRate
             *  redeemAmount = redeemAmountIn
             */
            redeemTokens = div_(redeemAmountIn, exchangeRate);
            redeemAmount = redeemAmountIn;
        }
        require(redeemAmount % expScale == 0, "invalid redeemTokens");

        uint redeemNFTCount = redeemAmount / expScale;

        /* Fail if redeem not allowed */
        uint allowed = comptroller.redeemAllowed(address(this), redeemer, redeemTokens);
        if (allowed != 0) {
            revert RedeemComptrollerRejection(allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            revert RedeemFreshnessCheck();
        }

        /* Fail gracefully if protocol has insufficient cash */
        if (getNFTsHeld() < redeemNFTCount) {
            revert RedeemTransferOutNotPossible();
        }

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        uint redeemInterestAmount = supplyInterestStoredInternal(redeemer);
        supplyInterest[redeemer].interestIndex = supplyIndex;

        /*
         * We write the previously calculated values into storage.
         *  Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.
         */
        totalSupply = totalSupply - redeemTokens;

        uint accountTokensNew = accountTokens[redeemer] - redeemTokens;
        accountTokens[redeemer] = accountTokensNew;

        uint256[] memory nftIds = doNFTTransferOut(redeemer, redeemNFTCount);

        if (redeemInterestAmount != 0) {
            supplyInterest[redeemer].interestAccrued = 0;
            interestMarket.collectInterest(redeemer, redeemInterestAmount);
        }

        /* We emit a Transfer event, and a Redeem event */
        emit Transfer(redeemer, address(this), redeemTokens);
        emit Redeem(redeemer, redeemNFTCount, redeemTokens, nftIds);

        /* We call the defense hook */
        comptroller.redeemVerify(address(this), redeemer, redeemAmount, redeemTokens);

        if (accountTokensNew == 0 && borrowBalanceStoredInternal(redeemer) == 0) {
            comptroller.autoExitMarkets(redeemer); // silent failure allowed
        }
    }

function doNFTTransferOut(address to, uint nftCount) virtual internal returns (uint256[] memory nftIds) {
        nftIds = new uint256[](nftCount);
        uint256 nftID;
        uint idx = heldNFTs.length;
        IERC721 underlying_ = IERC721(underlying);
        for(uint i = 0; i < nftCount;) {
            idx--;
            nftID = heldNFTs[idx];
            underlying_.transferFrom(address(this), to, nftID);
            heldNFTs.pop();
            nftIds[i] = nftID;
            unchecked { i++; }
        }
    }