# EMA-based Mark Price Component Exhibits Excessive Lag Leading to Inaccurate Pricing

Basis-spread EMA used 900 snapshots at 10s cadence, yielding a 2.5h window instead of 15min, lagging mark price and skewing funding rates.

- Payout: Contest reward
- Severity: medium
- Program: GTE
- Credited: BenRai
- Disclosed: 2026-02-05
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-08-gte-perps-and-launchpad#m-03
- Library page: https://bugbountylibrary.com/r/c4%3A2025-08-gte-perps-and-launchpad%3AM-03
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:oracle-manipulation

**Target:** Perpetuals mark price / basis spread EMA (Market.sol, PriceHistory.sol)
**Root cause:** getBasisSpreadEMA passes 15 minutes (900) as a snapshot count to ema(), but setMarkPrice runs every 10s, so 900 snapshots span 9000s (2.5h) instead of the intended 15 minutes.
**Steps:**
1. Note setMarkPrice() is called every 10s by admins, appending a basisSpread snapshot each call.
2. Read getBasisSpreadEMA: `basisSpreadHistory.ema(15 minutes)` = ema(900).
3. ema() treats 900 as number of snapshots, not seconds: 900 * 10s = 9000s = 2.5h window.
4. Observe mark price = median(p1 funding, p2 basis EMA, p3 impact TWAP); stale p2 lags rapid midPrice/indexPrice moves.
5. Mark price feeds funding rate, so funding is miscomputed during volatility.
**How found:** Code review of the EMA helper: noticed the 15-minute constant was consumed as a snapshot count while the update cadence is 10s, multiplying the effective window by 10x.
**Insight:** When an EMA/TWAP helper takes a 'period' argument, verify whether it means seconds or sample count and cross-check against the actual update cadence; unit mismatch silently scales the window.
