# TOFT triggerSendFrom can be used to steal all the balance

TOFT triggerSendFrom forwards attacker-controlled LayerZero adapterParams and refundAddress, letting anyone drain native-token TOFT balances via airdrop/refund to themselves.

- Payout: Contest reward
- Severity: high
- Program: Tapioca DAO
- Credited: windhustler
- Disclosed: 2023-11-16
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2023-07-tapioca-findings/issues/1290
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-tapioca%3AH-13
- 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:** BaseTOFT / TapiocaOFT cross-chain sendFrom pathway (LayerZero)
**Root cause:** triggerSendFrom accepts raw bytes adapterParams and refundAddress from the caller and forwards them to sendFromDestination, which calls sendFrom{value: address(this).balance} with attacker-controlled LzCallParams, so the whole contract balance is spent and any excess refunded to the attacker.
**Steps:**
1. Attacker calls `triggerSendFrom` on TapiocaOFT (native-token variant, erc20=address(0)) with `airdropAdapterParams` = airdropAdapterParamsV1 (deliver only, no airdrop) and `lzCallParams.adapterParams` = `abi.encodePacked(uint16(2), gasLimit, airdroppedAmount, attacker)` (adapterParamsV2).
2. Set `lzCallParams.refundAddress` = attacker address.
3. On destination, `sendFromDestination` executes `ISendFrom(address(this)).sendFrom{value: address(this).balance}(from, lzDstChainId, ..., callParams)`.
4. LayerZero airdrops the encoded amount to attacker on lzDstChainId and refunds excess value to refundAddress (attacker).
5. Repeat to drain past the per-tx max airdrop cap.
**How found:** Code reading of the triggerSendFrom -> sendFromDestination delegatecall path: noticed BaseTOFT delegatecalls BaseTOFTOptionsModule so msg.sender is the TOFT itself, and that raw adapterParams/refundAddress are user-supplied while sendFrom is invoked with the full contract balance.
**Exploitation:** Attacker submits triggerSendFrom with crafted adapterParamsV2 encoding max airdrop to their address and refundAddress set to themselves; destination-side sendFrom spends the entire TOFT native balance, airdropping capped amounts per call and refunding the remainder to the attacker, draining the contract over multiple calls.
**Escalation:** Repeated calls bypass the LayerZero per-message max airdrop cap to drain the full balance.
**Insight:** When a contract delegatecalls a module that then calls an external bridge/send function with `value: address(this).balance`, the caller controls refundAddress and adapterParams — check whether excess value is refunded to an attacker-supplied address and whether airdrop params can redirect funds cross-chain.
