# no-revert-on-transfer ERC20 tokens can be drained

Cally createVault used transferFrom for ERC721-typed vaults, letting a no-revert-on-failure ERC20 (ZRX) initialize a vault with zero tokens, so buyers paid premium/strike for nothing.

- Payout: Contest reward
- Severity: high
- Program: Cally
- Credited: Ruhum
- Disclosed: 2022-07-01
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-05-cally-findings/issues/89
- Library page: https://bugbountylibrary.com/r/c4%3A2022-05-cally%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Cally.sol createVault ERC20/ERC721 token handling
**Root cause:** createVault branches on caller-supplied TokenType; for ERC721 it calls transferFrom instead of safeTransferFrom, so an ERC20 returning false (ZRX) is accepted without reverting and the vault initializes with no deposited tokens.
**Steps:**
1. Call `createVault(100, 0xE41d2489571d322189246DaFA5ebDe1F4699F498, 1, 1, 1, 0, Cally.TokenType.ERC721)` with zero ZRX balance; transferFrom returns false but does not revert.
2. Vault is created with tokenIdOrAmount=100 though Cally holds no ZRX.
3. Buyer calls `buyOption{value: premium}(vaultId)` then `exercise{value: strike}(optionId)`.
4. Buyer receives zero ZRX but pays premium+strike; creator keeps the Ether.
**How found:** Reasoned about ERC20 tokens that return false instead of reverting, then noticed createVault's ERC721 branch uses transferFrom (which ignores the boolean) rather than safeTransferFrom, allowing a mismatched token type to bypass the deposit check.
**Exploitation:** Attacker creates a vault using a no-revert-on-failure ERC20 (e.g. ZRX) while declaring TokenType.ERC721, depositing nothing. An honest buyer purchases and exercises the option, paying premium and strike Ether but receiving no underlying tokens; the vault creator captures the buyer's funds.
**Insight:** When a contract lets the caller declare a token standard/type, test cross-type mismatches: an ERC20 passed as ERC721 hits transferFrom whose ignored bool return silently succeeds for non-reverting tokens like ZRX.
