# Approval is not reset if the call to IFulfillHelper fails

Connext fulfill() approves callTo for toSend tokens, but on IFulfillHelper.addFunds failure it transfers funds to fallback without revoking approval, letting callTo pull tokens later.

- Payout: Audit contest
- Severity: high
- Program: Connext
- Found by: pauliax
- Disclosed: 2021-08-30
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-07-connext-findings/issues/31
- Library page: https://bugbountylibrary.com/r/c4%3A2021-07-connext%3AH-05
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** Connext nxtp fulfill() cross-chain transaction settlement contract
**Root cause:** ERC20 approval to txData.callTo is granted before the try/catch around IFulfillHelper.addFunds; the catch branch transfers funds to the fallback address but never resets the approval, leaving a dangling allowance.
**Steps:**
1. Call fulfill() with a malicious txData.callTo and a receivingAssetId ERC20, toSend > 0.
2. Contract executes `LibERC20.approve(txData.receivingAssetId, txData.callTo, toSend)`.
3. IFulfillHelper(txData.callTo).addFunds reverts, entering catch.
4. Catch branch calls `LibAsset.transferAsset(txData.receivingAssetId, payable(txData.receivingAddress), toSend)` without revoking approval.
5. Malicious callTo later calls transferFrom to pull the still-approved toSend tokens.
**How found:** Code review of fulfill()'s approve-then-try/catch flow: noticed approval is set outside the try/catch and the catch path transfers funds without clearing it, leaving a stale allowance exploitable by the callTo.
**Exploitation:** Attacker controls callTo (a contract that reverts in addFunds). After fulfill() falls back to transferring to receivingAddress, the leftover allowance lets the attacker's contract pull toSend tokens via transferFrom, stealing funds from the contract/fallback flow.
**Insight:** When a contract approves a caller-controlled address before a try/catch, check every catch/fallback branch for approval revocation; a reverting helper leaves a live allowance that the same address can drain later.

