# Fee is being deducted when Put is expired and not when it is exercised.

PuttyV2 charges admin fee on expired unexercised puts and skips fee on exercised puts, mischarging users and leaking protocol revenue.

- Payout: Contest reward
- Severity: high
- Program: Putty
- Credited: zishansami
- Disclosed: 2022-06-27
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-06-putty-findings/issues/269
- Library page: https://bugbountylibrary.com/r/c4%3A2022-06-putty%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:fee-bypass

**Target:** PuttyV2.sol withdraw() and exercise() fee logic
**Root cause:** Fee base and timing are wrong: withdraw() deducts fee from strike for expired unexercised puts, while exercise() transfers full strike with no fee deduction.
**Steps:**
1. Read setFee() comment: fee applies on exercise.
2. Inspect withdraw() L495-L503: if (!order.isCall && !isExercised) and fee>0, feeAmount = strike*fee/1000 deducted from strike sent to msg.sender.
3. Inspect exercise() L451: full order.strike transferred to caller with no fee deduction.
4. Conclude expired puts are charged and exercised puts are not.
**How found:** Code reading of fee logic against the documented intent in setFee()'s comment ('fee rate that is applied on exercise'), spotting the mismatch between withdraw() and exercise() branches.
**Exploitation:** Not an attack; incorrect fee accounting causes asset loss to put shorter on expiry and protocol revenue leak on exercise.
**Insight:** When a fee is documented as applying at one lifecycle event, grep every branch that moves the fee base asset and verify the fee is applied exactly once at that event; strike vs premium as fee base is a recurring source of accounting bugs.
