# Incorrect ft reserve used in _buyToken leads to incorrect ft calculation and completely breaks protocol logic

Stale transient ft reserve in _buyToken ignores freshly minted ft, causing unnecessary debt issuance and corrupting swapCallback deltaFt accounting.

- Payout: Contest reward
- Severity: high
- Program: Term Structure
- Credited: BengalCatBalu
- Disclosed: 2025-03-20
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_term_structure_february2025.pdf#3.1.3
- Library page: https://bugbountylibrary.com/r/cantina%3Aterm_structure_february2025%3A3.1.3
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:precision-loss

**Target:** Solidity swap/mint contract (_buyToken/_buyExactToken)
**Root cause:** getTransientFtReserve() is snapshotted at swap start, but market.mint() increases the real ft balance before the reserve check, so the comparison uses a stale value and _issueFtToSelf writes an invalid targetFtReserve to transient storage.
**Steps:**
1. Call swapExactTokenToToken routing through _buyFt → _buyToken with tokenOut == ft.
2. Observe market.mint(address(this), debtTokenAmtIn) mints ft to the contract before the reserve check.
3. Compare getTransientFtReserve() (pre-mint snapshot) against ft.balanceOf(address(this)) (snapshot + debtTokenAmtIn).
4. When ftReserve < netOut + feeAmt, _issueFtToSelf issues extra ft via gt, over-borrowing debt.
5. setTransientFtReserve(targetFtReserve) stores a non-balance value, so swapCallback receives wrong deltaFt.
**How found:** Code reading of _buyToken: noticed market.mint precedes the ftReserve check, then reasoned the transient reserve snapshot predates the mint, and confirmed with console.log of Reserves vs Real Balance in forge test testSellFt.
**Exploitation:** Not a direct theft path; attacker triggers swaps where tokenOut == ft to force unnecessary debt issuance and feed corrupted deltaFt into Vault swapCallback calculations.
**Insight:** When a contract caches balances in transient storage at swap start, check every state-mutating call (mint/transfer) between the snapshot and its use — any balance change invalidates the cached reserve and downstream delta math.
