feat: implement and type-safe DriftTradingService for Solana/Drift trading (Node 20+)

This commit is contained in:
root
2025-07-09 10:47:47 +02:00
parent cfa6660abb
commit 6a1a4576a9
3 changed files with 3449 additions and 61 deletions

121
lib/drift-trading.ts Normal file
View File

@@ -0,0 +1,121 @@
import { Connection, Keypair } from '@solana/web3.js'
import {
DriftClient,
Wallet,
OrderType,
PositionDirection,
MarketType,
convertToNumber,
BASE_PRECISION,
PRICE_PRECISION,
BN,
type PerpPosition
} from '@drift-labs/sdk'
export interface TradeParams {
symbol: string
side: 'BUY' | 'SELL'
amount: number // USD amount
orderType?: 'MARKET' | 'LIMIT'
price?: number
}
export interface TradeResult {
success: boolean
txId?: string
error?: string
executedPrice?: number
executedAmount?: number
}
export interface Position {
symbol: string
side: 'LONG' | 'SHORT'
size: number
entryPrice: number
unrealizedPnl: number
}
export class DriftTradingService {
private connection: Connection
private wallet: Wallet
private driftClient: DriftClient
constructor() {
const rpcUrl = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com'
const secret = process.env.SOLANA_PRIVATE_KEY
if (!secret) throw new Error('Missing SOLANA_PRIVATE_KEY in env')
const keypair = Keypair.fromSecretKey(Buffer.from(JSON.parse(secret)))
this.connection = new Connection(rpcUrl, 'confirmed')
this.wallet = new Wallet(keypair)
this.driftClient = new DriftClient({
connection: this.connection,
wallet: this.wallet,
env: 'mainnet-beta',
opts: { commitment: 'confirmed' }
})
}
async executeTrade(params: TradeParams): Promise<TradeResult> {
try {
await this.driftClient.subscribe()
const marketIndex = await this.getMarketIndex(params.symbol)
const direction = params.side === 'BUY' ? PositionDirection.LONG : PositionDirection.SHORT
const orderType = params.orderType === 'LIMIT' ? OrderType.LIMIT : OrderType.MARKET
const price = params.price ? new BN(Math.round(params.price * PRICE_PRECISION.toNumber())) : undefined
const baseAmount = new BN(Math.round(params.amount * BASE_PRECISION.toNumber()))
const txSig = await this.driftClient.placeAndTakePerpOrder({
marketIndex,
direction,
baseAssetAmount: baseAmount,
orderType,
price,
marketType: MarketType.PERP
})
// Fetch fill price and amount (simplified)
return { success: true, txId: txSig }
} catch (e: any) {
return { success: false, error: e.message }
} finally {
await this.driftClient.unsubscribe()
}
}
async getPositions(): Promise<Position[]> {
await this.driftClient.subscribe()
const user = this.driftClient.getUser()
// Example: check first 10 market indices (should be replaced with actual market list)
const positions: Position[] = []
for (let marketIndex = 0; marketIndex < 10; marketIndex++) {
const p = user.getPerpPosition(marketIndex)
if (!p || p.baseAssetAmount.isZero()) continue
// TODO: Calculate unrealizedPnl if SDK exposes it
positions.push({
symbol: this.getSymbolFromMarketIndex(marketIndex),
side: p.baseAssetAmount.gt(new BN(0)) ? 'LONG' : 'SHORT',
size: convertToNumber(p.baseAssetAmount, BASE_PRECISION),
entryPrice: convertToNumber(p.quoteEntryAmount, PRICE_PRECISION),
unrealizedPnl: 0
})
}
await this.driftClient.unsubscribe()
return positions
}
// Helper: map symbol to market index (stub, should use Drift markets config)
private async getMarketIndex(symbol: string): Promise<number> {
// TODO: Replace with real mapping
if (symbol === 'BTCUSD') return 0
if (symbol === 'ETHUSD') return 1
throw new Error('Unknown symbol: ' + symbol)
}
// Helper: map market index to symbol (stub)
private getSymbolFromMarketIndex(index: number): string {
if (index === 0) return 'BTCUSD'
if (index === 1) return 'ETHUSD'
return 'UNKNOWN'
}
}
export const driftTradingService = new DriftTradingService()

3381
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,13 @@
"start": "next start"
},
"dependencies": {
"@drift-labs/sdk": "^2.126.0-beta.14",
"@prisma/client": "^6.11.1",
"@solana/web3.js": "^1.98.2",
"next": "15.3.5",
"prisma": "^6.11.1"
"openai": "^5.8.3",
"prisma": "^6.11.1",
"puppeteer": "^24.12.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -20,6 +24,6 @@
"eslint": "^9",
"eslint-config-next": "15.3.5",
"tailwindcss": "^4",
"typescript": "^5"
"typescript": "^5.8.3"
}
}