# Withdrawing to other chain when exercising options won’t work as expected, leading to DoS

UsdoOptionReceiverModule calls USDO's internal _sendPacket instead of tapOFT's sendPacket when bridging exercised options, burning USDO or reverting with DoS.

- Payout: Contest reward
- Severity: medium
- Program: Tapioca
- Credited: 0xadrii
- Disclosed: 2024-02-14
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2024-02-tapioca-judging/issues/125
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AM-27
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic vuln:dos

**Target:** UsdoOptionReceiverModule.exerciseOptionsReceiver / OFT _sendPacket
**Root cause:** When withdrawOnOtherChain is true, the module invokes its own USDO _sendPacket (which _debits/burns USDO from msg.sender) instead of calling tapOFT's sendPacket, so the exercised tapOFT is never bridged.
**Steps:**
1. User sends a PT_TAP_EXERCISE compose message with withdrawOnOtherChain=true and lzSendParams.
2. exerciseOptionsReceiver calls exerciseOption, receiving tapOFT into the USDO contract.
3. Instead of tapOFT.sendPacket, the module calls its internal _sendPacket, which invokes _debit and _burn(msg.sender, amountSentLD) on USDO.
4. msg.sender (LayerZero endpoint) lacks USDO balance, so `ERC20: burn amount exceeds balance` reverts, DoS'ing the flow.
5. If endpoint held USDO, it would be burned and tapOFT stuck in the contract.
**How found:** Code reading of exerciseOptionsReceiver's withdrawOnOtherChain branch revealed it calls the module's own _sendPacket (USDO OFT) rather than tapOFT's sendPacket, then traced _debit to _burn(msg.sender).
**Exploitation:** Any user exercising options with withdrawOnOtherChain=true triggers the revert; no attacker capital needed, the legitimate flow itself is broken.
**Insight:** In OFT/compose receivers, verify which token's sendPacket/_debit is invoked when bridging a different asset than the module's native token; the msg.sender of _debit is the endpoint, not the user, so balance assumptions break.
