Reality Cards — Audit Update

Andrew Stanger
Reality Cards
Published in
4 min readJul 3, 2021

Good morning, afternoon or evening, beautiful Reality Cards beta users.

Early June was an emotional time for the Reality Cards team, because after many long months of work, we waved goodbye to the smart contracts that power the Reality Cards protocol and sent them off to Audit College, for the final stages of their training.

The Reality Cards contracts heading off to Audit College

They faced five strict and highly disciplined examiners:

https://avolabs.io/
https://www.byterocket.dev/
https://www.certik.org/
https://code423n4.com/
https://hashcloak.com/

Professor Certik keeping a keen eye on the contracts (which have apparently turned into a girl)

Well we’re happy to report, that as of today, the smart contracts have returned from their training!

Findings

Given the size and complexity of the contracts (audits took a full month to complete), we are happy to report that there were surprisingly few major findings:

Missing sanity checks on rent collection argument

function collectRentUser(address _user, uint256 _timeToCollectTo) public {}

Full function here.

This is one of the most important functions in the protocol, and collects a user’s rent for all their Cards across all events. It is called by various other functions within the contract.

This function was missing sanity checks on _timeToCollectTo, allowing a timestamp well into the future to be passed. The net result was that it enabled an attacker to force the current owner of a Card to pay pay rent far into the future, potentially draining their entire deposit instantly.

Artist’s impression of a Card having been rented for a thousand years

Missing access controls on market sponsorship

function sponsor(address _sponsorAddress, uint256 _amount) external {}

Full function here.

Reality Cards has a sponsor function, allowing anyone to add funds to an event without the chance to win, as a way of increasing liquidity to incentivise betting. It is called via the createMarket() function by the Factory contract upon market creation- this is why it is passed the sponsor address, instead of using msg.sender. However, no checks were made that it was indeed being called by the Factory, thus it was possible to force an existing user to sponsor an event without their permission.

Dangling pointer in the orderbook

The Reality Cards orderbook is incredibly complex. For each Card, it stores all user bids- not just ‘active’ bids (i.e., current owners who are paying rent) but everyone else who has put a bid on the Card (an inactive bid), so the contract knows who to give ownership to in the case of the current owner’s deposit running out.

The data is stored in a doubly linked list ordered by price. For gas efficiency (and other reasons), this data is separated from all other market data, and is stored in a single contract (RCOrderbook.sol) which stores orderbook information for all Cards across all events.

The full contract can be seen here.

An extremely esoteric error (whose details are beyond the nature of this post) was discovered in the way bids were removed when a user ran out of deposit. In very specific circumstances (which could be engineered deliberately by an attacker) this ‘broke’ the linked list, preventing the contract discovering the correct next owner of a Card when the current owner’s deposit ran out, causing the contract to lock up.

Missing access controls on NFT upgrades

function upgradeCard(uint256 _card) external onlyTokenOwner(_card) {}

Full function here.

This function allows users to ‘upgrade’ their collected NFTs to mainnet after the event ends. It is called within each market contract. The problem was that although it checks that the caller owns the Card, no checks were made that the card belonged to this specific event. This allowed a user to ‘upgrade’ a Card they were currently renting (i.e. had not yet won) on a different market that had not yet ended. Only the longest owner should be able to upgrade Cards, and only after the event has ended.

Are my funds in the current beta #safu?

The current beta is using a very old version of the contracts created back in the Palaeolithic Era, which was not audited. None of the issues found are relevant to this older version. Of course, since the current beta was not audited, we cannot rule out any bugs, but we are not aware of anything that would impact it.

Can I see the reports?

Not yet. In the interests of safety, and in light of upcoming redeployments, we are not making the reports public until we have fixed all issues and had them re-audited. As soon as it is safe to do so we will be making all reports public.

What next?

We are working flat out to fix the above issues- and a laundry list of non-critical improvements- before they are resubmitted for a final audit. Then we launch!

Splidge, our Solidity Engineer, implementing the necessary fixes

--

--