fix: Increase transaction confirmation timeout to 60s for Alchemy Growth
- Alchemy Growth (10,000 CU/s) can handle longer confirmation waits - Increased timeout from 30s to 60s in both openPosition() and closePosition() - Added debug logging to execute endpoint to trace hang points - Configured dual RPC: Alchemy primary (transactions), Helius fallback (subscriptions) - Previous 30s timeout was causing premature failures during Solana congestion - This should resolve 'Transaction was not confirmed in 30.00 seconds' errors Related: User reported n8n webhook returning 500 with timeout error
This commit is contained in:
12
.env
12
.env
@@ -31,11 +31,15 @@ API_SECRET_KEY=2a344f0149442c857fb56c038c0c7d1b113883b830bec792c76f1e0efa15d6bb
|
|||||||
|
|
||||||
# Solana RPC URL (Required for blockchain access)
|
# Solana RPC URL (Required for blockchain access)
|
||||||
#
|
#
|
||||||
# RECOMMENDED: Helius (best performance, free tier available)
|
# PRIMARY: Alchemy Growth (10,000 CU/s, best for transactions)
|
||||||
# Get free API key at: https://helius.dev
|
# Used for: Transaction submission and confirmation
|
||||||
SOLANA_RPC_URL=https://mainnet.helius-rpc.com/?api-key=5e236449-f936-4af7-ae38-f15e2f1a3757
|
SOLANA_RPC_URL=https://solana-mainnet.g.alchemy.com/v2/5A0iA5UYpsmP9gkuezYeg
|
||||||
|
|
||||||
# Alternative RPC providers (if not using Helius):
|
# FALLBACK: Helius (for WebSocket subscriptions if needed)
|
||||||
|
# Used for: Account subscriptions (Drift SDK initialization)
|
||||||
|
SOLANA_FALLBACK_RPC_URL=https://mainnet.helius-rpc.com/?api-key=5e236449-f936-4af7-ae38-f15e2f1a3757
|
||||||
|
|
||||||
|
# Alternative RPC providers (reference):
|
||||||
#
|
#
|
||||||
# QuickNode: https://solana-mainnet.quiknode.pro/YOUR_ENDPOINT/
|
# QuickNode: https://solana-mainnet.quiknode.pro/YOUR_ENDPOINT/
|
||||||
# Alchemy: https://solana-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_KEY
|
# Alchemy: https://solana-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_KEY
|
||||||
|
|||||||
@@ -567,6 +567,17 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
// while orders are still being placed, leaving orphaned stop loss orders
|
// while orders are still being placed, leaving orphaned stop loss orders
|
||||||
let exitOrderSignatures: string[] = []
|
let exitOrderSignatures: string[] = []
|
||||||
try {
|
try {
|
||||||
|
console.log('🔍 DEBUG: About to call placeExitOrders()...')
|
||||||
|
console.log('🔍 DEBUG: Parameters:', {
|
||||||
|
symbol: driftSymbol,
|
||||||
|
positionSizeUSD,
|
||||||
|
entryPrice,
|
||||||
|
tp1Price,
|
||||||
|
tp2Price,
|
||||||
|
stopLossPrice,
|
||||||
|
direction: body.direction
|
||||||
|
})
|
||||||
|
|
||||||
const exitRes = await placeExitOrders({
|
const exitRes = await placeExitOrders({
|
||||||
symbol: driftSymbol,
|
symbol: driftSymbol,
|
||||||
positionSizeUSD: positionSizeUSD,
|
positionSizeUSD: positionSizeUSD,
|
||||||
@@ -584,6 +595,8 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
hardStopPrice: hardStopPrice,
|
hardStopPrice: hardStopPrice,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('🔍 DEBUG: placeExitOrders() returned:', exitRes.success ? 'SUCCESS' : 'FAILED')
|
||||||
|
|
||||||
if (!exitRes.success) {
|
if (!exitRes.success) {
|
||||||
console.error('❌ Failed to place on-chain exit orders:', exitRes.error)
|
console.error('❌ Failed to place on-chain exit orders:', exitRes.error)
|
||||||
} else {
|
} else {
|
||||||
@@ -594,10 +607,13 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
console.error('❌ Unexpected error placing exit orders:', err)
|
console.error('❌ Unexpected error placing exit orders:', err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('🔍 DEBUG: Exit orders section complete, about to calculate quality score...')
|
||||||
|
|
||||||
// Save trade to database FIRST (CRITICAL: Must succeed before Position Manager)
|
// Save trade to database FIRST (CRITICAL: Must succeed before Position Manager)
|
||||||
let qualityResult
|
let qualityResult
|
||||||
try {
|
try {
|
||||||
// Calculate quality score if metrics available
|
// Calculate quality score if metrics available
|
||||||
|
console.log('🔍 DEBUG: Calling scoreSignalQuality()...')
|
||||||
qualityResult = await scoreSignalQuality({
|
qualityResult = await scoreSignalQuality({
|
||||||
atr: body.atr || 0,
|
atr: body.atr || 0,
|
||||||
adx: body.adx || 0,
|
adx: body.adx || 0,
|
||||||
@@ -610,6 +626,9 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
timeframe: body.timeframe,
|
timeframe: body.timeframe,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('🔍 DEBUG: scoreSignalQuality() completed, score:', qualityResult.score)
|
||||||
|
console.log('🔍 DEBUG: About to call createTrade()...')
|
||||||
|
|
||||||
await createTrade({
|
await createTrade({
|
||||||
positionId: openResult.transactionSignature!,
|
positionId: openResult.transactionSignature!,
|
||||||
symbol: driftSymbol,
|
symbol: driftSymbol,
|
||||||
@@ -643,6 +662,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
indicatorVersion: body.indicatorVersion || 'v5', // Default to v5 for backward compatibility
|
indicatorVersion: body.indicatorVersion || 'v5', // Default to v5 for backward compatibility
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('🔍 DEBUG: createTrade() completed successfully')
|
||||||
console.log(`💾 Trade saved with quality score: ${qualityResult.score}/100`)
|
console.log(`💾 Trade saved with quality score: ${qualityResult.score}/100`)
|
||||||
console.log(`📊 Quality reasons: ${qualityResult.reasons.join(', ')}`)
|
console.log(`📊 Quality reasons: ${qualityResult.reasons.join(', ')}`)
|
||||||
} catch (dbError) {
|
} catch (dbError) {
|
||||||
|
|||||||
@@ -146,11 +146,18 @@ export async function openPosition(
|
|||||||
console.log(`📝 Transaction submitted: ${txSig}`)
|
console.log(`📝 Transaction submitted: ${txSig}`)
|
||||||
|
|
||||||
// CRITICAL: Confirm transaction actually executed on-chain
|
// CRITICAL: Confirm transaction actually executed on-chain
|
||||||
console.log('⏳ Confirming transaction on-chain...')
|
console.log('⏳ Confirming transaction on-chain (60s timeout for Alchemy Growth)...')
|
||||||
const connection = driftService.getConnection()
|
const connection = driftService.getConnection()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const confirmation = await connection.confirmTransaction(txSig, 'confirmed')
|
// Increased timeout from 30s to 60s for Alchemy Growth reliability
|
||||||
|
// Alchemy Growth (10,000 CU/s) can handle longer waits without timing out
|
||||||
|
const confirmationPromise = connection.confirmTransaction(txSig, 'confirmed')
|
||||||
|
const timeoutPromise = new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Transaction confirmation timeout')), 60000)
|
||||||
|
)
|
||||||
|
|
||||||
|
const confirmation = await Promise.race([confirmationPromise, timeoutPromise]) as any
|
||||||
|
|
||||||
if (confirmation.value.err) {
|
if (confirmation.value.err) {
|
||||||
console.error(`❌ Transaction failed on-chain:`, confirmation.value.err)
|
console.error(`❌ Transaction failed on-chain:`, confirmation.value.err)
|
||||||
@@ -558,13 +565,14 @@ export async function closePosition(
|
|||||||
|
|
||||||
// CRITICAL: Confirm transaction on-chain to prevent phantom closes
|
// CRITICAL: Confirm transaction on-chain to prevent phantom closes
|
||||||
// BUT: Use timeout to prevent API hangs during network congestion
|
// BUT: Use timeout to prevent API hangs during network congestion
|
||||||
console.log('⏳ Confirming transaction on-chain (30s timeout)...')
|
console.log('⏳ Confirming transaction on-chain (60s timeout for Alchemy Growth)...')
|
||||||
const connection = driftService.getConnection()
|
const connection = driftService.getConnection()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Increased timeout from 30s to 60s for Alchemy Growth reliability
|
||||||
const confirmationPromise = connection.confirmTransaction(txSig, 'confirmed')
|
const confirmationPromise = connection.confirmTransaction(txSig, 'confirmed')
|
||||||
const timeoutPromise = new Promise((_, reject) =>
|
const timeoutPromise = new Promise((_, reject) =>
|
||||||
setTimeout(() => reject(new Error('Transaction confirmation timeout')), 30000)
|
setTimeout(() => reject(new Error('Transaction confirmation timeout')), 60000)
|
||||||
)
|
)
|
||||||
|
|
||||||
const confirmation = await Promise.race([confirmationPromise, timeoutPromise]) as any
|
const confirmation = await Promise.race([confirmationPromise, timeoutPromise]) as any
|
||||||
@@ -577,7 +585,7 @@ export async function closePosition(
|
|||||||
console.log('✅ Transaction confirmed on-chain')
|
console.log('✅ Transaction confirmed on-chain')
|
||||||
} catch (timeoutError: any) {
|
} catch (timeoutError: any) {
|
||||||
if (timeoutError.message === 'Transaction confirmation timeout') {
|
if (timeoutError.message === 'Transaction confirmation timeout') {
|
||||||
console.warn('⚠️ Transaction confirmation timed out after 30s')
|
console.warn('⚠️ Transaction confirmation timed out after 60s')
|
||||||
console.warn(' Order may still execute - check Drift UI')
|
console.warn(' Order may still execute - check Drift UI')
|
||||||
console.warn(` Transaction signature: ${txSig}`)
|
console.warn(` Transaction signature: ${txSig}`)
|
||||||
// Continue anyway - order was submitted and will likely execute
|
// Continue anyway - order was submitted and will likely execute
|
||||||
@@ -654,7 +662,7 @@ export async function closePosition(
|
|||||||
async function retryWithBackoff<T>(
|
async function retryWithBackoff<T>(
|
||||||
fn: () => Promise<T>,
|
fn: () => Promise<T>,
|
||||||
maxRetries: number = 3,
|
maxRetries: number = 3,
|
||||||
baseDelay: number = 5000 // Increased from 2s to 5s: 5s → 10s → 20s progression
|
baseDelay: number = 8000 // Increased from 5s to 8s: 8s → 16s → 32s progression for better RPC recovery
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user