# ERC20 Incorrect check on returnedAddress in permit() results in unlimited approval of zero address

Swivel's Solmate-derived ERC20 permit() inverted its zero-address check, letting anyone grant themselves unlimited allowance over address(0) and drain tokens sent there.

- Payout: Contest reward
- Severity: medium
- Program: Swivel
- Credited: devtooligan
- Disclosed: 2022-09-29
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-07-swivel-findings/issues/72
- Library page: https://bugbountylibrary.com/r/c4%3A2022-07-swivel%3AM-07
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:incomplete-fix technique:source-review vuln:access-control vuln:signature-replay

**Target:** ERC20 permit() signature recovery in Creator/Erc20.sol
**Root cause:** Solmate's `require(recoveredAddress != address(0) && recoveredAddress == owner)` was converted to a custom error as `recoveredAddress != address(0) && recoveredAddress != owner`, so a zero recovered address passes instead of reverting.
**Steps:**
1. Call `permit(address(0x0), attacker, type(uint).max, block.timestamp, 0, bytes32(0), bytes32(0))` — v=0 makes ecrecover return address(0).
2. Check passes since recoveredAddress == 0, setting allowance[address(0)][attacker] = max.
3. Call `transferFrom(address(0x0), attacker, balanceOf(address(0x0)))` to pull any tokens held at the zero address.
4. Call `burnZcTokenRemovingNotional(...)` to redeem underlying, then repeat.
**How found:** Code review of the Solmate-derived ERC20 during the audit; noticed the require() was rewritten as a custom error with inverted logic, then reasoned that ecrecover returns address(0) for v=0.
**Exploitation:** Attacker self-approves over address(0) via forged permit, transfers any tokens mistakenly sent to address(0) to themselves, then burns zcTokens via the marketplace to redeem underlying; requires tokens to actually sit at address(0).
**Insight:** When a protocol forks Solmate/OpenZeppelin, diff the require()/revert conversions line by line — custom-error refactors frequently invert boolean logic, and ecrecover returning address(0) on malformed v is the classic trigger.
