# Unchecked ERC20 transfers can cause lock up

topupMarketBalance ignores ERC20 transfer return value, letting anyone inflate marketBalance and permanently trip the balancedBooks invariant, locking deposits, withdrawals, rent and payouts.

- Payout: Audit contest
- Severity: high
- Program: Reality Cards
- Found by: axic
- Disclosed: 2021-08-02
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-06-realitycards-findings/issues/2
- Library page: https://bugbountylibrary.com/r/c4%3A2021-06-realitycards%3AH-01
- Tags: area:web3 domain:web3 impact:dos kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:dos

**Target:** Solidity escrow/market contract topupMarketBalance and balancedBooks modifier
**Root cause:** topupMarketBalance calls erc20.transferFrom without checking its boolean return and lacks the balancedBooks modifier, so a false-returning token lets marketBalance be incremented without real funds moving.
**Steps:**
1. Identify that the contract uses a non-reverting ERC20 whose transferFrom returns false on failure.
2. Call `topupMarketBalance(_amount)` with an unrealistically large _amount that does not overflow marketBalance.
3. transferFrom returns false but is ignored; marketBalance is incremented anyway.
4. balancedBooks now requires balanceOf >= totalDeposits + marketBalance + totalMarketPots, which fails.
5. deposit, withdrawDeposit, payRent, payout, sponsor etc. revert with 'Books are unbalanced!'
**How found:** Code review of the balancedBooks invariant: noticed the modifier is applied to most functions but missing on topupMarketBalance, and that the transfer return value is unchecked, allowing the invariant to be desynced from real balances.
**Exploitation:** Attacker calls topupMarketBalance with a huge amount on a false-returning token; no tokens move but marketBalance rises above actual holdings, permanently breaking the balancedBooks check and locking all fund-moving functions.
**Insight:** When a contract enforces an accounting invariant via a modifier, audit every state-mutating function for the modifier's absence and for unchecked token return values that can desync tracked balances from real holdings.

