# Loss of funds in an underlying protocol would cause catostrophic loss of funds for swivel

Swivel's VaultTracker computes matured yield as maturityRate minus current exchangeRate; if the underlying vault's share price drops, redeemInterest pays out repeatedly, draining all Swivel funds.

- Payout: Contest reward
- Severity: medium
- Program: Swivel
- Credited: 0x52
- Disclosed: 2022-09-29
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-07-swivel-findings/issues/71
- Library page: https://bugbountylibrary.com/r/c4%3A2022-07-swivel%3AM-11
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** VaultTracker.sol / Swivel.sol yield-token accounting
**Root cause:** redeemInterest computes yield as maturityRate - vlt.exchangeRate and then sets vlt.exchangeRate = current rate without zeroing notional, assuming the underlying exchange rate never decreases.
**Steps:**
1. Mature a market so maturityRate is fixed at the then-current exchange rate (e.g. 1.25e6).
2. Underlying vault loses value; current exchange rate drops (e.g. 1.2e6).
3. Call `redeemInterest`; L132 sets vlt.exchangeRate = 1.2e6 and pays maturityRate - exchangeRate.
4. Call `redeemInterest` again; it pays the same difference again since notional is never cleared.
5. Repeat until all Swivel-deposited funds in the underlying vault are drained.
6. Alternatively, after the loss, use `SplitUnderlying` (no expiry check) to mint nTokens for the already-expired market, then run the loop.
**How found:** Code reading of VaultTracker yield math: noticed L124 assumes exchange rate is monotonically non-decreasing and that L132 updates exchangeRate without clearing notional, then reasoned about what happens if the underlying protocol loses value (hack, UST depeg).
**Exploitation:** Attacker waits for or induces a drop in the integrated vault's share price below maturityRate, then repeatedly calls redeemInterest (or first mints expired-market nTokens via SplitUnderlying) to extract the maturityRate-minus-current-rate delta each call, draining Swivel's position in the underlying protocol.
**Insight:** When a protocol caches a 'maturity rate' and pays the delta against a live exchange rate, check whether the live rate can ever fall below the cached value and whether the payout state is reset after each claim; also check whether expired markets can still be minted into.
