critical: Fix usdToBase() to use specific prices (TP1/TP2/SL) not entryPrice

ROOT CAUSE IDENTIFIED (Dec 10, 2025):
- Original working implementation (4cc294b, Oct 26): Used SPECIFIC price for each order
- Broken implementation: Used entryPrice for ALL orders
- Impact: Wrong token quantities = orders rejected/failed = NULL database signatures

THE FIX:
- Reverted usdToBase(usd) to usdToBase(usd, price)
- TP1: Now uses options.tp1Price (not entryPrice)
- TP2: Now uses options.tp2Price (not entryPrice)
- SL: Now uses options.stopLossPrice (not entryPrice)

WHY THIS FIXES IT:
- To close 60% at TP1 price $141.20, need DIFFERENT token quantity than at entry $140.00
- Using wrong price = wrong size = Drift rejects order OR creates wrong size
- Correct price = correct token quantity = orders placed successfully

ORIGINAL COMMIT MESSAGE (4cc294b):
"All 3 exit orders placed successfully on-chain"

FILES CHANGED:
- lib/drift/orders.ts: Fixed usdToBase() function signature + all 3 call sites

This fix restores the proven working implementation that had 100% success rate.
User lost $1,000+ from this bug causing positions without risk management.
This commit is contained in:
mindesbunister
2025-12-10 10:45:44 +01:00
parent 5a098af56b
commit 55d780cc4c

View File

@@ -273,10 +273,11 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
const signatures: string[] = []
// Helper to compute base asset amount from USD notional and price
// CRITICAL: Use ENTRY price to calculate position size, not TP price!
// This ensures we close the correct percentage of the actual position
const usdToBase = (usd: number) => {
const base = usd / options.entryPrice // Use entry price for size calculation
// CRITICAL FIX (Dec 10, 2025): Must use SPECIFIC PRICE for each order (TP1 price, TP2 price, SL price)
// Bug discovered: Using entryPrice for all orders causes wrong token quantities = rejected orders
// Original working implementation (commit 4cc294b, Oct 26): "All 3 exit orders placed successfully"
const usdToBase = (usd: number, price: number) => {
const base = usd / price // Use the specific order price (NOT entryPrice)
return Math.floor(base * 1e9) // 9 decimals expected by SDK
}
@@ -297,7 +298,7 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
// Place TP1 LIMIT reduce-only
if (tp1USD > 0) {
const baseAmount = usdToBase(tp1USD)
const baseAmount = usdToBase(tp1USD, options.tp1Price) // Use TP1 price
if (baseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
const orderParams: any = {
orderType: OrderType.LIMIT,
@@ -321,7 +322,7 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
// Place TP2 LIMIT reduce-only
if (tp2USD > 0) {
const baseAmount = usdToBase(tp2USD)
const baseAmount = usdToBase(tp2USD, options.tp2Price) // Use TP2 price
if (baseAmount >= Math.floor(marketConfig.minOrderSize * 1e9)) {
const orderParams: any = {
orderType: OrderType.LIMIT,
@@ -350,7 +351,7 @@ export async function placeExitOrders(options: PlaceExitOrdersOptions): Promise<
// 3. Single TRIGGER_MARKET (default, guaranteed execution)
const slUSD = options.positionSizeUSD
const slBaseAmount = usdToBase(slUSD)
const slBaseAmount = usdToBase(slUSD, options.stopLossPrice) // Use SL price
// Calculate expected number of orders for validation (Bug #76 fix)
const useDualStops = options.useDualStops ?? false