# Redeemer.sol#redeem() can be called by anyone before maturity, which may lead to loss of user funds

Illuminate Redeemer.redeem() is permissionless and callable before maturity, forcing discounted market sales of Notional principal and causing protocol fund loss.

- Payout: Contest reward
- Severity: high
- Program: Illuminate
- Credited: WatchPug
- Disclosed: 2022-08-23
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-06-illuminate-findings/issues/347
- Library page: https://bugbountylibrary.com/r/c4%3A2022-06-illuminate%3AH-14
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** Redeemer.sol redeem() contract function
**Root cause:** redeem() lacks a maturity check and is publicly callable; for Notional, redeeming before maturity triggers a market borrow/sale at a discount via previewRedeem.
**Steps:**
1. Call `Redeemer.redeem(p, u, m)` with p=Notional principal before market maturity m
2. Function reads principal balance of lender and transfers it to Redeemer
3. Calls `INotional(principal).maxRedeem(address(this))` which for non-matured assets sells on market at discount
4. Protocol holdings are liquidated at a loss to the caller's benefit
**How found:** Code review of redeem() noting absence of maturity gating combined with knowledge that Notional's previewRedeem performs a market borrow/sale when not matured.
**Exploitation:** Any external caller invokes redeem() pre-maturity for a Notional market; the contract redeems non-matured fCash via NotionalV2.getPrincipalFromfCashBorrow, effectively a discounted market sale, realizing a loss for Illuminate stakeholders.
**Insight:** When integrating external yield protocols, check whether their redeem/withdraw path behaves differently pre- vs post-maturity; a permissionless wrapper around a discounting redeem is a griefing/loss vector.
