critical: Bug #93 - Three-layer entry price validation with oracle fallback
Root Cause: quoteAssetAmount/baseAssetAmount division producing garbage entry prices - Found 6 autosync records with impossible prices (.18, 6.30, 7.11, 116.24 for SOL) - Drift SDK values can be corrupted during state transitions Fix Layer 1 (lib/drift/client.ts): - Added per-asset price range validation (SOL: 0-000, BTC: 0k-00k, ETH: 00-0k) - Returns null for invalid prices Fix Layer 2 (lib/trading/sync-helper.ts): - Added validatedEntryPrice calculation with oracle fallback - Falls back to Pyth oracle when calculated price is garbage Fix Layer 3 (lib/trading/sync-helper.ts): - Trade creation uses validatedEntryPrice in all 4 price fields - entryPrice, peakPrice, maxFavorablePrice, maxAdversePrice Documentation: - Full Bug #93 added to COMMON_PITFALLS.md with code examples - Quick Reference table updated Cleaned: 6 garbage autosync records deleted from database
This commit is contained in:
@@ -277,6 +277,22 @@ export class DriftService {
|
||||
// Calculate entry price
|
||||
const entryPrice = Math.abs(quoteAssetAmount / baseAssetAmount)
|
||||
|
||||
// BUG #89 FIX: Validate entry price is realistic for each asset
|
||||
// Reject garbage entry prices from stale/corrupted position data
|
||||
const priceValidation: { [key: number]: { min: number; max: number; asset: string } } = {
|
||||
0: { min: 50, max: 1000, asset: 'SOL' }, // SOL: $50-$1000 range
|
||||
1: { min: 10000, max: 500000, asset: 'BTC' }, // BTC: $10k-$500k range
|
||||
2: { min: 500, max: 20000, asset: 'ETH' }, // ETH: $500-$20k range
|
||||
}
|
||||
|
||||
const validation = priceValidation[marketIndex]
|
||||
if (validation && (entryPrice < validation.min || entryPrice > validation.max)) {
|
||||
console.error(`❌ INVALID ENTRY PRICE for ${validation.asset}: $${entryPrice.toFixed(2)} (expected $${validation.min}-$${validation.max})`)
|
||||
console.error(` Raw data: baseAsset=${baseAssetAmount}, quoteAsset=${quoteAssetAmount}`)
|
||||
console.error(` This indicates corrupted/stale position data - rejecting`)
|
||||
return null // Reject garbage position data
|
||||
}
|
||||
|
||||
// Get unrealized P&L
|
||||
const unrealizedPnL = Number(this.user!.getUnrealizedPNL(false, marketIndex)) / 1e6
|
||||
|
||||
|
||||
Reference in New Issue
Block a user