# PortcalFacet.repayAavePortal() can trigger an underflow of routerBalances

repayAavePortal subtracts user-controlled amountIn in an unchecked block, letting a router underflow routerBalances and arbitrarily inflate their balance.

- Payout: Audit contest
- Severity: high
- Program: Connext
- Found by: Ruhum
- Disclosed: 2022-10-17
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-06-connext-findings/issues/68
- Library page: https://bugbountylibrary.com/r/c4%3A2022-06-connext%3AH-01
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error

**Target:** PortalFacet.repayAavePortal() smart contract function
**Root cause:** When the local asset equals the adopted asset, swapFromLocalAssetIfNeededForExactOut returns amountIn == _backingAmount + _feeAmount (user-controlled), and this is subtracted from routerBalances inside an unchecked block. The only guard checks routerBalance < _maxIn, not the actual amountIn.
**Steps:**
1. Set _local to an asset where canonicalToAdopted[id] == _local so the swap short-circuits and returns amountIn = totalAmount.
2. Choose _backingAmount + _feeAmount such that totalAmount > s.routerBalances[msg.sender][_local].
3. Pass _maxIn <= s.routerBalances[msg.sender][_local] to satisfy the insufficientFunds check.
4. Call repayAavePortal(_local, _backingAmount, _feeAmount, _maxIn, _transferId).
5. unchecked subtraction underflows routerBalances, inflating the caller's balance arbitrarily.
**How found:** Code review of repayAavePortal's balance accounting: noticed the sanity check compares routerBalance to _maxIn rather than the actual amountIn, and that the no-swap edge case returns the user-supplied totalAmount, which then feeds an unchecked subtraction.
**Exploitation:** A registered portal router calls repayAavePortal with a local asset equal to its adopted asset and backing+fee exceeding its balance but _maxIn within it; the unchecked subtraction wraps, granting the router a huge internal balance usable elsewhere in the protocol.
**Insight:** When a function validates a user-supplied cap (_maxIn) but later subtracts a different, user-influenced value (amountIn), check whether a no-op/identity branch of the swap makes amountIn equal the attacker's input; unchecked blocks around such subtractions are prime underflow targets.

