# Wrong timing of check allows users to withdraw collateral without paying for the debt

Timeswap pay() checked collateral ratio using loop accumulators before they were populated, letting a borrower withdraw all collateral while repaying zero debt.

- Payout: Audit contest
- Severity: high
- Program: Timeswap
- Found by: WatchPug
- Disclosed: 2022-05-17
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-03-timeswap-findings/issues/16
- Library page: https://bugbountylibrary.com/r/c4%3A2022-03-timeswap%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** TimeswapPair.sol pay() collateral/debt ratio check
**Root cause:** The require comparing assetIn*debt vs collateralOut*collateral is evaluated inside the loop before assetIn/collateralOut are incremented, so on the first iteration both are 0 and the check trivially passes.
**Steps:**
1. Call `borrow()` with 1 BTC collateral to obtain 10,000 USDC debt
2. Call `pay()` with `param.assetsIn[0]=0` and `param.collateralsOut[0]=due.collateral`
3. Loop's first iteration: assetIn=0, collateralOut=0, so `require(0*debt >= 0*collateral)` passes
4. due.collateral is decremented to 0 and collateral is released while debt remains unpaid
**How found:** Code reading of the pay() loop: noticed the ratio require uses assetIn/collateralOut accumulators that are still zero on the first iteration, making the invariant vacuous.
**Exploitation:** Borrower takes a loan, then calls pay() with zero assetsIn and full collateralOut; the misordered ratio check passes on the first iteration, releasing all collateral while the debt is never repaid, netting the borrowed principal.
**Insight:** When an invariant check inside a loop references accumulators that are updated later in the same iteration, the first pass is always trivially satisfied; move such checks after the loop or validate per-iteration values.
