# Possible incentive theft through the arbitraryCall() function

Locke.arbitraryCall could pre-approve an incentive token before createIncentive received it, letting a malicious recipient transferFrom and steal deposited incentives.

- Payout: Audit contest
- Severity: high
- Program: Streaming Protocol
- Found by: toastedsteaksandwich
- Disclosed: 2022-02-11
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-11-streaming-findings/issues/199
- Library page: https://bugbountylibrary.com/r/c4%3A2021-11-streaming%3AH-05
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** Locke.sol governance arbitraryCall and createIncentive incentive token handling
**Root cause:** The incentive-token guard in arbitraryCall only blocks calls when the incentives mapping already shows a balance, so a token can be approved before any incentive is created.
**Steps:**
1. Identify a token likely to be used as an incentive.
2. Call `arbitraryCall()` to `token.approve(maliciousRecipient, max)` while incentives[token] balance is zero, bypassing the guard at Locke.sol#L735.
3. Wait for a user to call `createIncentive()` depositing that token into the contract.
4. Malicious recipient calls `transferFrom(victim/contract, attacker, amount)` to drain the deposited incentive tokens.
**How found:** Reading the arbitraryCall guard, noticed it only checks current incentive balance, so pre-funding approval state bypasses the check.
**Exploitation:** Governance uses arbitraryCall to set an allowance on a not-yet-funded incentive token; after a user deposits via createIncentive, the pre-approved spender pulls the tokens with transferFrom.
**Insight:** State-dependent guards that check 'balance now' can be bypassed by acting before the state exists; look for approval/allowance setup that persists across the guard window.
