# triggerSendFrom() will send all the ETH in the destination chain where sendFrom() is called to the refundAddress in the LzCallParams argument

BaseTOFTOptionsModule.sendFromDestination forwards address(this).balance as msg.value to sendFrom, so LayerZero refunds the entire tOFT ETH balance to an attacker-chosen refundAddress.

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

**Target:** BaseTOFTOptionsModule / BaseTOFT sendFrom cross-chain flow
**Root cause:** sendFromDestination() calls sendFrom with value: address(this).balance instead of only the required LayerZero fee; sendFrom passes _callParams.refundAddress to _send, and LayerZero refunds excess msg.value to that address.
**Steps:**
1. Attacker calls `triggerSendFrom{value: fee}(lzDstChainId, adapterParams, zroPaymentAddress, amount, callParams, approvals)` with `callParams.refundAddress = attacker`.
2. Payload `PT_SEND_FROM` is delivered to destination chain; `sendFromDestination` executes `sendFrom{value: address(this).balance}(from, lzDstChainId, ..., callParams)`.
3. LayerZero UltraLightNodeV2 refunds `msg.value - fee` to `callParams.refundAddress` (attacker) on the chain holding the ETH.
4. Attacker approves tOFT for `amount` tokens so sendFrom succeeds; attacker balance increases by full tOFT ETH balance minus fee.
**How found:** Code reading of sendFromDestination revealed it passes address(this).balance as msg.value; tracing sendFrom's use of _callParams.refundAddress and LayerZero's excess-refund behavior showed the whole balance is refundable to an arbitrary address.
**Exploitation:** Single cross-chain triggerSendFrom with attacker-controlled refundAddress; destination-chain sendFromDestination spends the contract's whole ETH balance as msg.value, and LayerZero refunds the excess to the attacker on the source chain. No capital beyond the initial LZ fee and token approval.
**Insight:** When a contract forwards `address(this).balance` as msg.value to a function that refunds excess to a caller-supplied address, the entire balance becomes claimable; check every payable call whose refund recipient is user-controlled.
