File size: 2,235 Bytes
9e22644 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | import { NextResponse } from 'next/server'
import { fetchWalletSignals, isHeliusConfigured } from '@/lib/helius'
import { calculateChurnScore } from '@/lib/agent-engine'
import { sendCustomEvent, isTorqueConfigured } from '@/lib/torque-mcp'
import { pushEvent } from '@/lib/event-store'
import { recordIntervention } from '@/lib/attribution-store'
export async function POST(req: Request) {
const { address, autoFire = false } = await req.json()
if (!address || typeof address !== 'string') {
return NextResponse.json({ error: 'address required' }, { status: 400 })
}
if (!isHeliusConfigured()) {
return NextResponse.json({ error: 'HELIUS_API_KEY not configured' }, { status: 503 })
}
const signals = await fetchWalletSignals(address)
if (!signals) {
return NextResponse.json({ error: 'Failed to fetch wallet data from Helius' }, { status: 502 })
}
const { score, risk, signals: detectedSignals } = calculateChurnScore({
daysInactive: signals.daysInactive,
volumeDropPct: signals.volumeDropPct,
uniqueProtocols: signals.uniqueProtocols,
currentStreak: signals.currentStreak,
hasLiquidation: signals.hasLiquidation,
})
let torqueResult: { fired: boolean; eventId?: string; eventName?: string } = { fired: false }
if (autoFire && isTorqueConfigured() && (risk === 'critical' || risk === 'high' || risk === 'medium')) {
const eventName = risk === 'critical' || risk === 'high' ? 'churn_risk_high' : 'churn_risk_medium'
const result = await sendCustomEvent(address, eventName, {
risk, score,
daysInactive: signals.daysInactive,
protocols: signals.protocols,
source: 'helius_live',
detectedBy: 'flowstate-helius-scan',
})
if (result.success && result.eventId) {
pushEvent({
ingestionId: result.eventId,
wallet: address,
eventName,
risk,
score,
firedAt: new Date().toISOString(),
source: 'scan',
})
recordIntervention(address, eventName, score)
torqueResult = { fired: true, eventId: result.eventId, eventName }
}
}
return NextResponse.json({
address,
signals,
score,
risk,
detectedSignals,
torque: torqueResult,
})
}
|