# Exercise option cross chain message in the (m)TapiocaOFT will always revert in the destination, losing debited funds in the source chain

BaseTOFT encodes exercise() call without the leading `module` address arg, so every PT_TAP_EXERCISE cross-chain message reverts on destination while source funds are debited and lost.

- Payout: Contest reward
- Severity: high
- Program: Tapioca DAO
- Credited: 0x73696d616f
- Disclosed: 2023-11-16
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-07-tapioca-findings/issues/1069
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-tapioca%3AH-29
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:business-logic

**Target:** BaseTOFT / BaseTOFTOptionsModule cross-chain exercise handler (LayerZero lzReceive)
**Root cause:** BaseTOFTOptionsModule.exercise() signature includes a leading `address module` parameter, but _nonblockingLzReceive encodes the selector with only (_srcChainId, _srcAddress, _nonce, _payload), so ABI decoding fails and the delegatecall reverts.
**Steps:**
1. User calls `exerciseOption{value:1 ether}(optionsData, lzData, tapSendData, approvals)` on TapiocaOFT; source chain debits funds and emits PT_TAP_EXERCISE payload.
2. Destination receives payload; `_nonblockingLzReceive` matches `packetType == PT_TAP_EXERCISE` and builds `abi.encodeWithSelector(BaseTOFTOptionsModule.exercise.selector, _srcChainId, _srcAddress, _nonce, _payload)`.
3. Delegatecall to options module fails ABI decoding because `exercise(address module, uint16, bytes, uint64, bytes)` expects 5 args; revert selector `0x4e487b71...41` (panic) emitted via MessageFailed.
4. Source-chain debited funds are never credited on destination and are permanently lost.
**How found:** Code reading: compared the exercise() function declaration (with extra `address module` arg) against the abi.encodeWithSelector call in _nonblockingLzReceive, spotting the argument-count mismatch; confirmed with a Foundry fork PoC replaying lzReceive.
**Exploitation:** No attacker action needed; any legitimate user exercising options cross-chain triggers the revert, permanently losing the debited source-chain funds. PoC forks mainnet, wraps 1 ether, calls exerciseOption, then replays lzReceive as the LayerZero endpoint and asserts MessageFailed.
**Insight:** When a module function is invoked via delegatecall with abi.encodeWithSelector, diff the selector's parameter list against the actual function signature — an extra leading arg (often an injected `module`/`self` address) silently breaks every cross-chain message and can strand user funds.
