# TOFTOptionsReceiverModule will have the user lose the whole output TAP when requested to exercise all eligible options

TOFTOptionsReceiverModule forwards only _options.tapAmount to the user; with tapAmount=0 the full exercised TAP stays frozen in the contract.

- Payout: Contest reward
- Severity: high
- Program: Tapioca
- Credited: ctf_sec
- Disclosed: 2024-02-14
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2024-02-tapioca-judging/issues/130
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AH-12
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** TOFTOptionsReceiverModule.exerciseOptionsReceiver cross-chain options exercise
**Root cause:** exerciseOptionsReceiver uses the caller-supplied _options.tapAmount to decide how much TAP to forward, but TapiocaOptionBroker treats tapAmount=0 as 'exercise full eligible amount', so the actual received TAP is never forwarded.
**Steps:**
1. Call exerciseOptionsReceiver with _options.tapAmount = 0 (valid full-exercise workflow).
2. Broker's exerciseOption computes chosenAmount = eligibleTapAmount and processes the OTC deal, minting TAP to the receiver contract.
3. Receiver computes amountToSend = min(_send.amountLD, 0) = 0 (or transfers 0 on same chain).
4. All exercised TAP remains in the contract balance with no rescue path; oTAPTokenID eligible amount is now zero so it cannot be re-exercised.
**How found:** Code review comparing the receiver's forwarding logic against TapiocaOptionBroker's documented tapAmount==0 semantics; noticed the receiver trusts _options.tapAmount rather than the realized balance delta.
**Exploitation:** User legitimately exercises full option position via cross-chain receiver with tapAmount=0; contract receives the TAP output but forwards nothing, permanently freezing the user's proceeds.
**Insight:** When a downstream contract treats 0 as a sentinel ('use full amount'), any wrapper that reuses that parameter for its own payout math will underpay; compare balance before/after the external call instead of trusting the input.
