# Unchecked low-level call success

Low-level call or swap trusted on success flag alone, without return data, code, or balance checks

_Area: Smart contract access control and control flow. Seen in 3 disclosed reports; latest 2024-05-23. Updated 2026-10-31._

## How it works
A low-level `.call` returns `success=true` in cases that do not mean the intended work happened: the target has no code, the selector resolves to a view or unrelated function, or the callee returns without delivering value. Code that branches only on the boolean treats "the EVM did not revert" as "the operation succeeded." The broken assumption is that call success equals semantic success. It does not: success only means execution did not revert, and return data, target code, and post-call balances are separate facts the caller must check.

## Where it shows up
- Swap/aggregator libraries calling routers via `.call` and checking only the boolean [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md)[E3](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-11.md).
- Calldata that can target any function on a whitelisted router, including view functions like `getAmountsOut()` [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md).
- Batch/multicall executors that forward `msg.value` per action and swallow reverts with an `allowFailure` flag [E1](https://bugbountylibrary.com/r/c4%3A2024-02-tapioca%3AM-16.md).
- Missing contract-existence check before a call to a possibly empty address [E3](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-11.md).
- No post-swap balance delta assertion at inner or outer layer [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md).

## How to review
For every low-level call, confirm the code checks more than the success flag:
- Target has code (`extcodesize > 0`) before calling [E3](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-11.md).
- Return data is decoded and validated where the callee is expected to return a result.
- Function selectors are whitelisted, not just router addresses [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md).
- Post-call balance deltas prove tokens actually arrived [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md).
- In batch executors, `msg.value` accounting refunds value for failed or unused actions, so contract balance returns to zero [E1](https://bugbountylibrary.com/r/c4%3A2024-02-tapioca%3AM-16.md).
The invariant: after any call path, no caller's value remains stranded in the contract, and success implies the intended state change occurred.

## Variants seen
- Failed action with `allowFailure=true` leaves ETH stuck, claimable by the next caller [E1](https://bugbountylibrary.com/r/c4%3A2024-02-tapioca%3AM-16.md).
- Non-swapping function on a whitelisted router returns success without delivering tokens [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md).
- Call to a non-existent contract returns success, so a failed swap does not revert [E3](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-11.md).

## Impact and severity
User funds are taken or stranded while the transaction reports success: lost swap principal [E2](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md)[E3](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-11.md), or ETH left in the contract and stealable by other users [E1](https://bugbountylibrary.com/r/c4%3A2024-02-tapioca%3AM-16.md). Severity is high where funds move on the trusted success flag; worse when one facet's missing check exposes a shared library used elsewhere [E3](https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-11.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2024-02-tapioca%3AM-16.md
- E2: https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AH-02.md
- E3: https://bugbountylibrary.com/r/c4%3A2022-03-lifinance%3AM-11.md
