xanoutas/solidity-security-findings huggingface dataset:
Market-specific pause is not checked for sponsor
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

cmichel


# Vulnerability details

## Vulnerability Details

The treasury only checks its `globalPause` field but does not check its market-specific `marketPaused` field for `Treasury.sponsor`.
A paused market contract can therefore still deposit as a sponsor using `Market.sponsor`

## Impact

The market-specific pause does not work correctly.

## Recommended Mitigation Steps

Add checks for `marketPaused` in the Treasury for `sponsor`.
Market-specific pause is not checked for payout
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

cmichel


# Vulnerability details

## Vulnerability Details

The treasury only checks its `globalPause` field but does not check its market-specific `marketPaused` field for `Treasury.payout`.
A paused market contract can therefore still pay out using `payArtist`, `payCardAffiliate`, `payMarketCreator`, `payAffiliate`,  `withdraw`

## Impact

The market-specific pause does not work correctly.

## Recommended Mitigation Steps

Add checks for `marketPaused` in the Treasury for `payout`.
Market-specific pause is not checked for payRent
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

cmichel


# Vulnerability details


## Vulnerability Details

The treasury only checks its `globalPause` field but does not check its market-specific `marketPaused` field for `Treasury.payRent`.
The market contract can therefore still collect rent even when it's paused using `Market.updateTimeHeldLimit`, `Market.exit` and `Market.collectRentAllCards`. (`treasury.marketPaused` is only checked in `Market.newRental`)

## Impact

The market-specific pause does not work correctly.

## Recommended Mitigation Steps

Add checks for `marketPaused` in the Treasury for `payRent`.
minRentalDayDivisor may change
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

pauliax


# Vulnerability details

## Impact
Market initializes minRentalDayDivisor from the treasury but this variable can be later changed in treasury (function setMinRental). Here I am not sure if the market still needs to use old cached version of minRentalDayDivisor as all the interactions with the treasury will operate on the new value while market still uses the old:
   minRentalDayDivisor = treasury.minRentalDayDivisor();
An example where I see a potential problem:
      require(
        treasury.userDeposit(_user) >=
            _userTotalBidRate / minRentalDayDivisor,
        "Insufficient deposit"
      );
Here, the market fetches userDeposit from the treasury but uses cached minRentalDayDivisor to check the sufficient balance. If minRentalDayDivisor is updated in the treasury, this check may become ineffective.

## Recommended Mitigation Steps
Do not use a cached version of minRentalDayDivisor in the Market contract, always fetch the latest value from the Factory contract.
Check that marketLockingTime >= marketOpeningTime
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

pauliax


# Vulnerability details

## Impact
There is no check that _timestamps[1] (marketLockingTime) >= _timestamps[0] (marketOpeningTime). I think that should be enforced just in case to prevent market locking before opening.

## Recommended Mitigation Steps
Add in Factory createMarket:
require(
            _timestamps[1] >= _timestamps[0],
            "Market locks too early"
        );
Wrong calculation on _collectRentAction
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

a_delamo


# Vulnerability details

## Impact

The method `_collectRentAction` contains the following code:
```
...
           } else if (!_foreclosed && _limitHit && _marketLocked) {
                // CASE 4
                // didn't foreclose AND
                // did hit time limit AND
                // did lock market
                // THEN refund rent between the earliest event and now
                if (_cardTimeLimitTimestamp < marketLockingTime) {
                    // time limit hit before market locked
                    _timeOfThisCollection = _cardTimeLimitTimestamp;
                    _newOwner = true;
                    _refundTime = block.timestamp - _cardTimeLimitTimestamp;
                } else {
                    // market locked before time limit hit
                    _timeOfThisCollection = marketLockingTime;
                    _newOwner = false;
                    _refundTime = block.timestamp - marketLockingTime;
                }
            } else if (_foreclosed && !_limitHit && !_marketLocked) {
                // CASE 5
                // did foreclose AND
                // didn't hit time limit AND
                // didn't lock market
                // THEN rent OK, find new owner
                _timeOfThisCollection = _timeUserForeclosed;
                _newOwner = true;
                _refundTime = 0;
            } else if (_foreclosed && !_limitHit && _marketLocked) {
                // CASE 6
                // did foreclose AND
                // didn't hit time limit AND
                // did lock market
                // THEN if foreclosed first rent ok, otherwise refund after locking
                if (_timeUserForeclosed < marketLockingTime) {
                    // user foreclosed before market locked
                    _timeOfThisCollection = _timeUserForeclosed;
                    _newOwner = true;
                    _refundTime = 0;
                } else {
                    // market locked before user foreclosed
                    _timeOfThisCollection = marketLockingTime;
                    _newOwner = false;
                    _refundTime = block.timestamp - marketLockingTime;
                }
            } else if (_foreclosed && _limitHit && !_marketLocked) {
                // CASE 7
                // did foreclose AND
                // did hit time limit AND
                // didn't lock market
                // THEN if foreclosed first rent ok, otherwise refund after limit
                if (_timeUserForeclosed < _cardTimeLimitTimestamp) {
                    // user foreclosed before time limit
                    _timeOfThisCollection = _timeUserForeclosed;
                    _newOwner = true;
                    _refundTime = 0;
                } else {
                    // time limit hit before user foreclosed
                    _timeOfThisCollection = _cardTimeLimitTimestamp;
                    _newOwner = true;
                    _refund
Missing checkOnERC721Received deviates from ERC721 and could lock/lose NFTs
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

ERC721 specification for safeTransferFrom says: “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)”)).” This is implemented by the widely-used OpenZeppelin implementation which is also inherited here.

However, the overriding implementation in L2 NFT Hub is missing the check implemented by “require(_checkOnERC721Received(..))” after _transfer() to evaluate if the destination contract was indeed ERC-721 compatible.

Impact: NFT is transferred to a non ERC-721 compatible contract and gets locked/lost.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/nfthubs/RCNftHubL2.sol#L223-L234

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/00128bd26061986d10172573ceec914a4f3b4d3c/contracts/token/ERC721/ERC721.sol#L211-L212

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/00128bd26061986d10172573ceec914a4f3b4d3c/contracts/token/ERC721/ERC721.sol#L174-L213

https://eips.ethereum.org/EIPS/eip-721


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Add require(_checkOnERC721Received(..))” after _transfer() on L232.
NFT Hub implementation deviates from ERC721 for transfer functions
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

ERC721 standard and implementation allows the use of approved addresses to affect transfers besides the token owners. However, the L2 NFT Hub implementation deviates from ERC721 by ignoring the presence of any approvers in the overriding function implementations of transferFrom() and safeTransferFrom(). 

Impact: The system interactions with NFT platforms may not work if they expect ERC721 adherence. Users who interact via approved addresses will see their transfers failing for their approved addresses. 

Given that the key value proposition of this project is the use of NFTs, the expectation will be that it is fully compatible with ERC721.


## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/nfthubs/RCNftHubL2.sol#L212-L234

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/00128bd26061986d10172573ceec914a4f3b4d3c/contracts/token/ERC721/ERC721.sol#L158


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Add support for approval in NFT transfers.
Missing call to removeUserFromOrderbook after user is foreclosed
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

Orderbook’s removeUserFromOrderbook is used to delete/remove user’s bids when they are deemed foreclosed. This is called in Market newRental() and Treasury withdrawDeposit when users are determined to be foreclosed given their deposit and bid situation.

However, there is a missing call to removeUserFromOrderbook after user is marked foreclosed in collectRentUser. This will allow user's bids to remain in the order book and unless this removal is triggered by someone else, their bids will continue to stay and will affect future ownership calculations.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L736-L737

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCOrderbook.sol#L572-L629

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCMarket.sol#L687-L690

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L356-L366


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Add call to removeUserFromOrderbook after user is marked foreclosed in collectRentUser()
Missing balancedBooks modifier could result in failed system insolvency detection
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

The balancedBooks modifier is used to “check that funds haven't gone missing during this function call” and is applied to deposit, withdrawDeposit, payRent, payout and sponsor Treasury functions which move funds in and out of the Treasury or adjust its market/user balances.

However, this modifier is missing on refundUser() and topupMarketBalance() functions which also perform similar actions.

Impact: Any miscalculations in these functions will lead to the system becoming insolvent.


## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L123-L132

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L447-L450

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L372

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L282

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L325

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L409

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L432

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L469


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Add modifier to the two functions above where it is missing.
Missing call to removeOldBids may affect foreclosure
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

Orderbook.removeBids() as commented “///remove bids in closed markets for a given user ///this can reduce the users bidRate and chance to foreclose”

removeOldBids() is performed currently in Market.newRental() and Treasury.deposit() to  “do some cleaning up, it might help cancel their foreclosure” as commented. However, this is missing in the withdrawDeposit() function where the need is the most because user is removing deposit which may lead to foreclosure and is even commented as being useful on L356.

Impact: If we do not remove closed market bids during withdrawDeposit, the closed market bids still get accounted in user's bidrate in the conditional on L357 and therefore do not prevent the foreclosure in withdrawDeposit that may happen in L357-L367. User may get foreclosed because of mis-accounted closed-market bids in the order book.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCOrderbook.sol#L671-L713

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L356

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L357-L367

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCMarket.sol#L704-L705

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L300-L301


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Add call to removeOldBids() on L355 of withdrawDeposit() of Treasury.
Deposit whitelist enforced on msg.sender instead of user
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

The Treasury deposit() function credits amount to the user address in parameter instead of the msgSender() that is actually making the deposit whose rationale as explained in the Natspec comment is that this may be called via contract or L1-L2 bot.

However, the deposit whitelist should ideally be enforced on the _user address. If msgSender() is blacklisted, user address can still deposit() from another whitelisted msgSender() address while retaining the user address that is used for leader boards and NFTs.

Impact: User misbehaves in interactions with the system (e.g. trolls, spams) and their corresponding msgSender() is removed from the whitelist. User continues to deposit into the system via another whitelisted msgSender() without any impact to leader boards or NFTs.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L70-L71

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L204-L221

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L279-L297


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Use whitelist on user address instead of msgSender().
Critical uberOwner address changes should be a two-step process
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

As specified, uberOwners of Factory, Orderbook and Treasury have the highest privileges in the system because they can upgrade contracts of market, Nfthub, order book, treasury, token and factory which form the critical components of the protocol.

The contracts allow for uberOwners to be changed to a different address from the contract owner/deployer using the changeUberOwner() function which is callable by the current uberOwner. While this function checks for zero-address, there is no validation of the new address being correct. If the current uberOwner incorrectly uses an invalid address for which they do not have the private key, then the system gets locked because the uberOwner cannot be corrected and none of the other functions that require uberOwner caller can be executed.

Impact: The current uberOwner uses a non-zero but incorrect address as the new uberOwner. This gets set and now the system is locked and none of the uberOwner-only callable functions are callable. This error cannot be fixed either and will require redeployment of contracts which will mean that all existing markets have to be terminated. The system will have to be shut and restarted completely from scratch which will take a reputation hit and have a serious technical and business impact.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards#mortar_board-governance-mortar_board

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCFactory.sol#L444-L449

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCOrderbook.sol#L117-L121

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L264-L268


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Change the single-step change of uberOwner address to a two-step process where the current uberOwner first approves a new address as a pendingUberOwner. That pendingUberOwner has to then claim the ownership in a separate transaction which cannot be done if they do not have the correct private key. An incorrectly set pendingUberOwner can be reset by changing it again to the correct one who can then successfully claim it in the second step.
Missing threshold check on critical protection mechanism minRentalDayDivisor
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

Minimum rental duration is acknowledged as one of the two critical protection mechanisms for the market functioning. The setMinRental() is called from the constructor with 24*6 which sets the minimum duration to 10 minutes.

However, a threshold check is missing for minRentalDayDivisor in setMinRental() which can be called by the owner at any time. Without this check, this may be accidentally set to a value too high which makes duration very small. This will break the safety assumption of the markets.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L113
https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L167-L171


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Enforce a threshold check to make sure this is not set to too high a value which will break safety assumptions of the markets.
Malicious user can trigger another user’s removal
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

By allowing anyone to call removeUserFromOrderbook instead of only Market::newRental or Treasury::withdrawDeposit or collectRentUser which may result in foreclosures and hence may need to trigger user removal, a malicious user can trigger another's removal by front-running the user's deposit or exit actions to prevent foreclosure.

Impact: Alice is about to be foreclosed but has submitted a deposit transaction. Evil Eve notices Alice’s pending foreclosure and front-runs her deposit to trigger removeUserFromOrderbook. Alice is removed from the order book and markets.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCOrderbook.sol#L572-L579


## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Restrict access to this function only to those functions that may cause user foreclosure and hence need to remove user from order book.
function topupMarketBalance should have a balancedBooks modifier
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

pauliax


# Vulnerability details

## Impact
I expect every function in the Treasury that interacts with erc20 tokens to have this balancedBooks modifier to make sure that "funds haven't gone missing during this function call". To make sure that tokens were indeed transferred and marketBalance updated, this modifier can be used in function topupMarketBalance.

## Recommended Mitigation Steps
Add 'balancedBooks' to the declaration of function topupMarketBalance.
Flows can bypass market and global pause
Audit: 2021-06-realitycards-findings
Severity: MEDIUM

# Handle

0xRajeev


# Vulnerability details

## Impact

Ability to pause all token transfers and all state changes for contracts is a “guarded-launch” best-practice for emergency situations for newly launched projects. The project implements this using a marketsPaused flag per market and a globalPause flag across all markets. 

While these prevent renting of cards in a specific market and deposit/withdraw/rent cards across all markets, there are still public/external functions that are unguarded by these flags which can affect contract state in paused scenarios that will make it hard/impossible to recover correctly from the emergency pause.

Examples: topupMarketBalance() and refundUser() in Treasury can be triggered even in a globalPause scenario. There could be other function flows where it is not obvious that market/global pausing is enabled because it is enforced in one of the functions called deep within the code within one of the conditionals.

Impact: Markets get paused but the contracts cannot be restarted because of state changes affected during the pause via unguarded external/public functions.

## Proof of Concept

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L370-L380

https://github.com/code-423n4/2021-06-realitycards/blob/86a816abb058cc0ed9b6f5c4a8ad146f22b8034c/contracts/RCTreasury.sol#L446-L463

## Tools Used

Manual Analysis

## Recommended Mitigation Steps

Apply marketPaused and globalPause check clearly in the beginning of all public/external functions which move tokens/funds in/out or change contract state in any way.

Validate all possible control flows to check that market/global pausing works in all scenarios and applies to all contract state and not specific functionalities.