# A user with a TapiocaOFT allowance >0 could steal all the underlying ERC20 tokens of the owner

TapiocaOFT._wrap checks the OFT allowance but never decrements it, letting a spender repeatedly wrap the owner's ERC20 to drain all funds.

- Payout: Contest reward
- Severity: high
- Program: Tapioca DAO
- Credited: dirk\_y
- Disclosed: 2023-11-16
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-07-tapioca-findings/issues/219
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-tapioca%3AH-58
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** TapiocaOFT / BaseTOFT _wrap ERC20 wrapping contract
**Root cause:** _wrap verifies allowance(_fromAddress, msg.sender) on the TapiocaOFT token but never decrements it; the underlying ERC20 safeTransferFrom decrements the contract's own allowance, not the spender's.
**Steps:**
1. Owner Alice approves TapiocaOFT contract to spend her underlying ERC20 (commonly max approval).
2. Alice approves Bob for 5 TapiocaOFT tokens.
3. Bob calls `wrap(aliceAddress, bobAddress, 5)` repeatedly.
4. Each call passes the allowance check (never decremented) and mints 5 OFT to Bob while pulling 5 underlying tokens from Alice.
5. Repeat until Alice's entire ERC20 balance is drained.
**How found:** Code reading of _wrap: noticed the allowance check uses the OFT's own allowance mapping rather than the underlying ERC20 allowance, and that safeTransferFrom decrements the contract's allowance instead of the spender's, so the check is never consumed.
**Exploitation:** Spender with any non-zero TapiocaOFT allowance repeatedly calls wrap() with the owner as _fromAddress, minting wrapped tokens to themselves and pulling the owner's underlying ERC20 each time, since the allowance is never reduced.
**Insight:** When a wrapper contract pulls tokens on behalf of a third party, verify which allowance is checked versus which is actually decremented; a check against the wrapper's own token allowance is not consumed by an underlying safeTransferFrom.
