# exerciseOptionsReceiver() Lack of Ownership Check for oTAP, Allowing Anyone to Use oTAPTokenID

exerciseOptionsReceiver lacks ownership check on oTAPTokenID, letting anyone front-run a public permit signature to exercise another user's oTAP option.

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

**Target:** USDO cross-chain option receiver module (UsdoOptionReceiverModule.exerciseOptionsReceiver)
**Root cause:** exerciseOptionsReceiver trusts the caller-supplied _options.from and oTAPTokenID without verifying that _options.from owns or is approved for the oTAP NFT, so any approved USDO allowance on the token can be consumed by anyone.
**Steps:**
1. Alice sends lzSend to chain B with composeMsg: `oTAP.permit(usdo, oTAPTokenID, v, r, s)`, `exerciseOptionsReceiver(oTAPTokenID, _options.from=alice)`, `oTAP.revokePermit(oTAPTokenID)`
2. Bob observes the public permit signature (v, r, s) in the cross-chain message
3. Bob submits his own lzSend to chain B with `oTAP.permit(usdo, oTAPTokenID, v, r, s)` and `exerciseOptionsReceiver(oTAPTokenID, _options.from=bob)`
4. USDO.lzCompose executes permit then exerciseOptionsReceiver with srcChainSender=bob, exercising Alice's oTAPTokenID and refunding to Bob
**How found:** Code review of exerciseOptionsReceiver noticed it uses _options.from and oTAPTokenID without ownership validation, and that the required oTAP.permit signature is public in the cross-chain compose message, enabling front-running.
**Exploitation:** Attacker front-runs the victim's cross-chain compose message, reusing the publicly visible oTAP.permit signature to grant USDO approval on the victim's oTAPTokenID, then calls exerciseOptionsReceiver with _options.from set to the attacker so the option is exercised and any refund flows to the attacker.
**Insight:** When a cross-chain compose flow relies on a public permit signature to grant an approval, any caller can replay that signature; check that the NFT owner or approved party matches the caller-supplied beneficiary before consuming the approval.
