# calculate-cumulative-discount-factor will over-inflate cumulative discount factor overtime making loans accrue more interest

Off-by-one in recursive cumulative discount factor loop in Circuit's Chialisp utils.clib inflates interest factor each oracle update, overcharging borrowers.

- Payout: Contest reward
- Severity: medium
- Program: Circuit Puzzles
- Credited: 0xrex
- Disclosed: 2025-08-21
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_circuit_puzzles.pdf#3.3.9
- Library page: https://bugbountylibrary.com/r/cantina%3Acircuit_puzzles%3A3.3.9
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Chialisp smart contract (utils.clib / statutes_update_price.clsp)
**Root cause:** The recursive calculate-cumulative-discount-factor only terminates when previous_timestamp > current_timestamp, not >=, so it applies one extra per-minute compounding step per call.
**Steps:**
1. Read `calculate-cumulative-discount-factor` in `utils.clib`: recursion guard is `(if (> previous_timestamp current_timestamp) ...)`
2. Note loop increments `previous_timestamp` by `ONE_MINUTE` and only stops when strictly greater than `current_timestamp`
3. For a 30-minute gap, loop runs 31 iterations instead of 30
4. Each `statutes_update_price.clsp` oracle update persists the inflated `cumulative_stability_fee_df` via `create-coin-condition`
5. Over many updates the factor drifts above true elapsed time, inflating interest charged on vaults
**How found:** Code review of the recursive discount-factor function; noticed the termination condition uses strict `>` rather than `>=`, causing an extra iteration when timestamps align.
**Escalation:** Repeated oracle price updates compound the per-update one-minute overcount, so a 5-year 10% loan effectively charges ~13-15%.
**Insight:** In recursive per-tick compounding loops, check the loop's termination predicate against the exact boundary (equal timestamps) — an off-by-one tick compounds silently across every persisted state update.
