# Governance NFT holder, whose NFT was minted before Trading._handleOpenFees function is called, can lose deserved rewards after Trading._handleOpenFees function is called

Trading._handleOpenFees mints DAO fees but never approves GovNFT to pull them, so distribute() silently fails and early governance NFT holders lose rewards.

- Payout: Contest reward
- Severity: medium
- Program: Tigris Trade
- Credited: rbserver
- Disclosed: 2023-02-17
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-12-tigris-findings/issues/649
- Library page: https://bugbountylibrary.com/r/c4%3A2022-12-tigris%3AM-23
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:reward-distribution

**Target:** Trading.sol / GovNFT.sol reward distribution
**Root cause:** _handleOpenFees mints _daoFeesPaid to the Trading contract and calls gov.distribute without first calling IStable(_tigAsset).approve(address(gov), ...), unlike _handleCloseFees. GovNFT.distribute's transferFrom therefore reverts and is swallowed by try/catch, so accRewardsPerNFT is never incremented.
**Steps:**
1. Mint a GovNFT to owner before any trade (totalSupply=1).
2. Call `trading.initiateMarketOrder(TradeInfo, PriceData, sig, PermitData, owner)` which routes to `_handleOpenFees`.
3. Observe `FeesDistributed` emits daoFees=10000000000000000000 but `gov.distribute` transferFrom fails silently (no approval).
4. Check `govnft.pending(owner, stabletoken)` returns 0 instead of the deserved 10e18.
5. Apply fix `IStable(_tigAsset).approve(address(gov), type(uint).max)` before distribute; pending becomes 10e18.
**How found:** Code-reading comparison: noticed _handleCloseFees approves gov before distribute while _handleOpenFees does not, then traced GovNFT.distribute's try/catch transferFrom to confirm silent failure and wrote a Foundry/Hardhat PoC.
**Insight:** When two sibling fee-handling paths differ in an ERC20 approve() call, check whether the downstream puller uses transferFrom inside try/catch — silent failures there turn missing approvals into lost accounting rather than reverts.
