# Residual token allowance after external call

Approval to caller-supplied target or router not zeroed after the call, leaving spendable allowance

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

## How it works
A contract grants an allowance to an external address (a fill target, router, or swap caller) so that address can pull tokens for one operation. The assumption that breaks is that the allowance is consumed exactly once and fully. When the operation spends less than approved, or the approval is never reset, the leftover allowance stays live on the contract. The approved address can then call transferFrom again at any later time and pull the remaining balance, without any new authorization.

## Where it shows up
- `safeApprove`/`forceApprove` to a target derived from caller input, followed by an external call, with no zeroing afterward [E1](https://bugbountylibrary.com/r/sherlock%3A2024-11-oku%3AH-6.md)[E3](https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.5.md).
- Approval amount set to a maximum (`amountInMaximum`) while actual spend is smaller [E3](https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.5.md).
- Reset logic that covers only one token or one direction of a paired function (wrap resets one token, unwrap resets the other) [E3](https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.5.md).
- Refund paths that return unspent tokens to the order creator while the approval to the target persists [E1](https://bugbountylibrary.com/r/sherlock%3A2024-11-oku%3AH-6.md).
- Any contract holding a balance that an approved third party can reach via transferFrom.

## How to review
The invariant: after every external call, the allowance the contract granted to that target is zero (or exactly the amount consumed). Check each approve/call pair and confirm a reset to zero follows on all paths, including reverts and partial fills. Confirm the reset covers every token and both directions of paired functions [E3](https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.5.md). Confirm the approved spender is not attacker-controlled or caller-supplied without this reset [E1](https://bugbountylibrary.com/r/sherlock%3A2024-11-oku%3AH-6.md). For permission/quota systems, confirm one logical action consumes one use, not one per address checked [E2](https://bugbountylibrary.com/r/sherlock%3A2024-05-andromeda-ado%3AM-2.md).

## Variants seen
- Residual approval to an attacker-controlled fill target, drained via transferFrom [E1](https://bugbountylibrary.com/r/sherlock%3A2024-11-oku%3AH-6.md).
- Residual router allowance after wrap/unwrap, exploitable by a malicious router or compromised caller [E3](https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.5.md).
- Quota/allowance consumed multiple times per single authorization check, draining limited uses faster than intended [E2](https://bugbountylibrary.com/r/sherlock%3A2024-05-andromeda-ado%3AM-2.md).

## Impact and severity
In E1 and E3 the residual allowance let the approved party pull the contract's full token balance, a direct fund loss. Severity is high when the spender is attacker-influenced and the contract holds funds; it worsens when approvals are set to maximums and when reset logic is asymmetric across paired functions, widening the window and the set of tokens exposed.

## Why fixes fail
E3 shows partial fixes: resetting one token's approval while leaving the other, or fixing one of two paired functions, leaves the residual allowance reachable through the unfixed path.

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2024-11-oku%3AH-6.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2024-05-andromeda-ado%3AM-2.md
- E3: https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.2.5.md
