sol-trade-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +390 -0
- package/dist/chunk-MMQAMIKR.mjs +3735 -0
- package/dist/chunk-NEZDFAYA.mjs +7744 -0
- package/dist/clients-VITWK7B6.mjs +1370 -0
- package/dist/index-1BK_FXsW.d.mts +2327 -0
- package/dist/index-1BK_FXsW.d.ts +2327 -0
- package/dist/index.d.mts +2659 -0
- package/dist/index.d.ts +2659 -0
- package/dist/index.js +13265 -0
- package/dist/index.mjs +562 -0
- package/dist/perf/index.d.mts +2 -0
- package/dist/perf/index.d.ts +2 -0
- package/dist/perf/index.js +3742 -0
- package/dist/perf/index.mjs +214 -0
- package/package.json +101 -0
- package/src/__tests__/complete_sdk.test.ts +354 -0
- package/src/__tests__/hotpath.test.ts +486 -0
- package/src/__tests__/nonce.test.ts +45 -0
- package/src/__tests__/sdk.test.ts +425 -0
- package/src/address-lookup/index.ts +197 -0
- package/src/cache/cache.ts +308 -0
- package/src/calc/index.ts +1058 -0
- package/src/calc/pumpfun.ts +124 -0
- package/src/common/bonding_curve.ts +272 -0
- package/src/common/compute-budget.ts +148 -0
- package/src/common/confirm-any-signature.ts +184 -0
- package/src/common/fast-timing.ts +481 -0
- package/src/common/fast_fn.ts +150 -0
- package/src/common/gas-fee-strategy.ts +253 -0
- package/src/common/map-pool.ts +23 -0
- package/src/common/nonce.ts +40 -0
- package/src/common/sdk-log.ts +460 -0
- package/src/common/seed.ts +381 -0
- package/src/common/spl-token.ts +578 -0
- package/src/common/subscription-handle.ts +644 -0
- package/src/common/trading-utils.ts +239 -0
- package/src/common/wsol-manager.ts +325 -0
- package/src/compute/compute_budget_manager.ts +187 -0
- package/src/compute/index.ts +21 -0
- package/src/constants/index.ts +96 -0
- package/src/execution/execution.ts +532 -0
- package/src/execution/index.ts +42 -0
- package/src/hotpath/executor.ts +464 -0
- package/src/hotpath/index.ts +64 -0
- package/src/hotpath/state.ts +435 -0
- package/src/index.ts +2117 -0
- package/src/instruction/bonk_builder.ts +730 -0
- package/src/instruction/index.ts +24 -0
- package/src/instruction/meteora_damm_v2_builder.ts +509 -0
- package/src/instruction/pumpfun_builder.ts +1183 -0
- package/src/instruction/pumpswap.ts +1123 -0
- package/src/instruction/raydium_amm_v4_builder.ts +692 -0
- package/src/instruction/raydium_cpmm_builder.ts +795 -0
- package/src/middleware/traits.ts +407 -0
- package/src/params/index.ts +483 -0
- package/src/perf/compiler-optimization.ts +529 -0
- package/src/perf/hardware.ts +631 -0
- package/src/perf/index.ts +9 -0
- package/src/perf/kernel-bypass.ts +656 -0
- package/src/perf/protocol.ts +682 -0
- package/src/perf/realtime.ts +592 -0
- package/src/perf/simd.ts +668 -0
- package/src/perf/syscall-bypass.ts +331 -0
- package/src/perf/ultra-low-latency.ts +505 -0
- package/src/perf/zero-copy.ts +589 -0
- package/src/pool/pool.ts +294 -0
- package/src/rpc/client.ts +345 -0
- package/src/sdk-errors.ts +13 -0
- package/src/security/index.ts +26 -0
- package/src/security/secure-key.ts +303 -0
- package/src/security/validators.ts +281 -0
- package/src/seed/pda.ts +262 -0
- package/src/serialization/index.ts +28 -0
- package/src/serialization/serialization.ts +288 -0
- package/src/swqos/clients.ts +1754 -0
- package/src/swqos/index.ts +50 -0
- package/src/swqos/providers.ts +1707 -0
- package/src/trading/core/async-executor.ts +702 -0
- package/src/trading/core/confirmation-monitor.ts +711 -0
- package/src/trading/core/index.ts +82 -0
- package/src/trading/core/retry-handler.ts +683 -0
- package/src/trading/core/transaction-pool.ts +780 -0
- package/src/trading/executor.ts +385 -0
- package/src/trading/factory.ts +282 -0
- package/src/trading/index.ts +30 -0
- package/src/types.ts +8 -0
- package/src/utils/index.ts +155 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trade Executor for Sol Trade SDK
|
|
3
|
+
* Implements core trading execution with parallel SWQOS submissions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Connection,
|
|
8
|
+
Transaction,
|
|
9
|
+
TransactionInstruction,
|
|
10
|
+
PublicKey,
|
|
11
|
+
Keypair,
|
|
12
|
+
BlockhashWithExpiryBlockHeight,
|
|
13
|
+
Commitment,
|
|
14
|
+
} from '@solana/web3.js';
|
|
15
|
+
import {
|
|
16
|
+
SwqosClient,
|
|
17
|
+
ClientFactory,
|
|
18
|
+
SwqosClientConfig,
|
|
19
|
+
} from '../swqos/clients';
|
|
20
|
+
import {
|
|
21
|
+
GasFeeStrategy,
|
|
22
|
+
GasFeeStrategyType,
|
|
23
|
+
SwqosType,
|
|
24
|
+
TradeType,
|
|
25
|
+
} from '../common/gas-fee-strategy';
|
|
26
|
+
|
|
27
|
+
// ===== Types =====
|
|
28
|
+
|
|
29
|
+
export interface TradeResult {
|
|
30
|
+
signature: string;
|
|
31
|
+
success: boolean;
|
|
32
|
+
error?: string;
|
|
33
|
+
confirmationTimeMs?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ExecutorOptions {
|
|
37
|
+
waitConfirmation: boolean;
|
|
38
|
+
maxRetries: number;
|
|
39
|
+
retryDelayMs: number;
|
|
40
|
+
parallelSubmit: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function defaultExecutorOptions(): ExecutorOptions {
|
|
44
|
+
return {
|
|
45
|
+
waitConfirmation: true,
|
|
46
|
+
maxRetries: 3,
|
|
47
|
+
retryDelayMs: 100,
|
|
48
|
+
parallelSubmit: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface TradeConfig {
|
|
53
|
+
rpcUrl: string;
|
|
54
|
+
swqosConfigs: SwqosClientConfig[];
|
|
55
|
+
gasFeeStrategy?: GasFeeStrategy;
|
|
56
|
+
confirmationTimeoutMs?: number;
|
|
57
|
+
confirmationRetryCount?: number;
|
|
58
|
+
/** RPC commitment for `Connection` (default `confirmed`); align with main `TradeConfig`. */
|
|
59
|
+
commitment?: Commitment;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface BuildTransactionOptions {
|
|
63
|
+
payer: PublicKey;
|
|
64
|
+
recentBlockhash: string;
|
|
65
|
+
instructions: TransactionInstruction[];
|
|
66
|
+
signers: Keypair[];
|
|
67
|
+
gasConfig?: GasFeeConfig;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface GasFeeConfig {
|
|
71
|
+
computeUnitLimit: number;
|
|
72
|
+
computeUnitPrice: number;
|
|
73
|
+
priorityFee: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ===== Trade Executor =====
|
|
77
|
+
|
|
78
|
+
export class TradeExecutor {
|
|
79
|
+
private clients: Map<SwqosType, SwqosClient> = new Map();
|
|
80
|
+
private connection: Connection;
|
|
81
|
+
private gasStrategy?: GasFeeStrategy;
|
|
82
|
+
private confirmationTimeout: number;
|
|
83
|
+
private confirmationRetry: number;
|
|
84
|
+
|
|
85
|
+
constructor(private config: TradeConfig) {
|
|
86
|
+
this.connection = new Connection(config.rpcUrl, {
|
|
87
|
+
commitment: config.commitment ?? 'confirmed',
|
|
88
|
+
});
|
|
89
|
+
this.gasStrategy = config.gasFeeStrategy;
|
|
90
|
+
this.confirmationTimeout = config.confirmationTimeoutMs || 30000;
|
|
91
|
+
this.confirmationRetry = config.confirmationRetryCount || 30;
|
|
92
|
+
this.initializeClients();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private initializeClients(): void {
|
|
96
|
+
for (const swqosConfig of this.config.swqosConfigs) {
|
|
97
|
+
const client = ClientFactory.createClient(swqosConfig, this.config.rpcUrl);
|
|
98
|
+
this.clients.set(swqosConfig.type, client);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
addClient(config: SwqosClientConfig): void {
|
|
103
|
+
const client = ClientFactory.createClient(config, this.config.rpcUrl);
|
|
104
|
+
this.clients.set(config.type, client);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
removeClient(swqosType: SwqosType): void {
|
|
108
|
+
this.clients.delete(swqosType);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getClient(swqosType: SwqosType): SwqosClient | undefined {
|
|
112
|
+
return this.clients.get(swqosType);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async execute(
|
|
116
|
+
tradeType: TradeType,
|
|
117
|
+
transaction: Buffer,
|
|
118
|
+
opts: ExecutorOptions = defaultExecutorOptions()
|
|
119
|
+
): Promise<TradeResult> {
|
|
120
|
+
if (this.clients.size === 0) {
|
|
121
|
+
return {
|
|
122
|
+
signature: '',
|
|
123
|
+
success: false,
|
|
124
|
+
error: 'No SWQOS clients configured',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (opts.parallelSubmit) {
|
|
129
|
+
return this.executeParallel(tradeType, transaction, opts);
|
|
130
|
+
}
|
|
131
|
+
return this.executeSequential(tradeType, transaction, opts);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private async executeParallel(
|
|
135
|
+
tradeType: TradeType,
|
|
136
|
+
transaction: Buffer,
|
|
137
|
+
opts: ExecutorOptions
|
|
138
|
+
): Promise<TradeResult> {
|
|
139
|
+
const promises: Promise<TradeResult>[] = [];
|
|
140
|
+
|
|
141
|
+
for (const client of this.clients.values()) {
|
|
142
|
+
promises.push(this.submitToClient(client, tradeType, transaction, opts));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Race all submissions - first successful wins
|
|
146
|
+
try {
|
|
147
|
+
const results = await Promise.allSettled(promises);
|
|
148
|
+
for (const result of results) {
|
|
149
|
+
if (result.status === 'fulfilled' && result.value.success) {
|
|
150
|
+
return result.value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// All failed
|
|
154
|
+
const lastError = results.find(r => r.status === 'rejected') as PromiseRejectedResult;
|
|
155
|
+
return {
|
|
156
|
+
signature: '',
|
|
157
|
+
success: false,
|
|
158
|
+
error: lastError?.reason?.toString() || 'All parallel submissions failed',
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return {
|
|
162
|
+
signature: '',
|
|
163
|
+
success: false,
|
|
164
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private async executeSequential(
|
|
170
|
+
tradeType: TradeType,
|
|
171
|
+
transaction: Buffer,
|
|
172
|
+
opts: ExecutorOptions
|
|
173
|
+
): Promise<TradeResult> {
|
|
174
|
+
for (let retry = 0; retry < opts.maxRetries; retry++) {
|
|
175
|
+
for (const client of this.clients.values()) {
|
|
176
|
+
const result = await this.submitToClient(client, tradeType, transaction, opts);
|
|
177
|
+
if (result.success) {
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (retry < opts.maxRetries - 1) {
|
|
183
|
+
await this.sleep(opts.retryDelayMs);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
signature: '',
|
|
189
|
+
success: false,
|
|
190
|
+
error: `All sequential submissions failed after ${opts.maxRetries} retries`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private async submitToClient(
|
|
195
|
+
client: SwqosClient,
|
|
196
|
+
tradeType: TradeType,
|
|
197
|
+
transaction: Buffer,
|
|
198
|
+
opts: ExecutorOptions
|
|
199
|
+
): Promise<TradeResult> {
|
|
200
|
+
const startTime = Date.now();
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const signature = await client.sendTransaction(
|
|
204
|
+
tradeType,
|
|
205
|
+
transaction,
|
|
206
|
+
false
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
const result: TradeResult = {
|
|
210
|
+
signature,
|
|
211
|
+
success: true,
|
|
212
|
+
confirmationTimeMs: Date.now() - startTime,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
if (opts.waitConfirmation) {
|
|
216
|
+
const confirmed = await this.waitForConfirmation(signature);
|
|
217
|
+
result.confirmationTimeMs = Date.now() - startTime;
|
|
218
|
+
if (!confirmed) {
|
|
219
|
+
result.success = false;
|
|
220
|
+
result.error = 'Transaction failed to confirm';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return result;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return {
|
|
227
|
+
signature: '',
|
|
228
|
+
success: false,
|
|
229
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
230
|
+
confirmationTimeMs: Date.now() - startTime,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Rust `poll_any_transaction_confirmation`: success when err is none and status is confirmed or finalized. */
|
|
236
|
+
private async waitForConfirmation(signature: string): Promise<boolean> {
|
|
237
|
+
const startTime = Date.now();
|
|
238
|
+
const timeoutMs = this.confirmationTimeout;
|
|
239
|
+
|
|
240
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
241
|
+
try {
|
|
242
|
+
const status = await this.connection.getSignatureStatus(signature);
|
|
243
|
+
if (status.value) {
|
|
244
|
+
if (status.value.err) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
const cs = status.value.confirmationStatus;
|
|
248
|
+
if (cs === 'confirmed' || cs === 'finalized') {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch {
|
|
253
|
+
// Continue polling on error
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
await this.sleep(1000);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private sleep(ms: number): Promise<void> {
|
|
263
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async executeMultiple(
|
|
267
|
+
tradeType: TradeType,
|
|
268
|
+
transactions: Buffer[],
|
|
269
|
+
opts: ExecutorOptions = defaultExecutorOptions()
|
|
270
|
+
): Promise<TradeResult[]> {
|
|
271
|
+
return Promise.all(
|
|
272
|
+
transactions.map(tx => this.execute(tradeType, tx, opts))
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ===== Gas Fee Management =====
|
|
277
|
+
|
|
278
|
+
getGasConfig(
|
|
279
|
+
swqosType: SwqosType,
|
|
280
|
+
tradeType: TradeType,
|
|
281
|
+
strategyType: GasFeeStrategyType
|
|
282
|
+
): GasFeeConfig {
|
|
283
|
+
if (!this.gasStrategy) {
|
|
284
|
+
return {
|
|
285
|
+
computeUnitLimit: 200000,
|
|
286
|
+
computeUnitPrice: 100000,
|
|
287
|
+
priorityFee: 100000,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const value = this.gasStrategy.get(swqosType, tradeType, strategyType);
|
|
292
|
+
if (!value) {
|
|
293
|
+
return {
|
|
294
|
+
computeUnitLimit: 200000,
|
|
295
|
+
computeUnitPrice: 100000,
|
|
296
|
+
priorityFee: 100000,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
computeUnitLimit: value.cuLimit,
|
|
302
|
+
computeUnitPrice: value.cuPrice,
|
|
303
|
+
priorityFee: Math.floor(value.tip * 1_000_000_000), // Convert SOL to lamports
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ===== Utility Methods =====
|
|
308
|
+
|
|
309
|
+
async getLatestBlockhash(): Promise<BlockhashWithExpiryBlockHeight> {
|
|
310
|
+
return this.connection.getLatestBlockhash();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
getConnection(): Connection {
|
|
314
|
+
return this.connection;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ===== Rate Limiter =====
|
|
319
|
+
|
|
320
|
+
export class RateLimiter {
|
|
321
|
+
private lastSubmit: number = 0;
|
|
322
|
+
|
|
323
|
+
constructor(private minDelayMs: number) {}
|
|
324
|
+
|
|
325
|
+
async wait(): Promise<void> {
|
|
326
|
+
const now = Date.now();
|
|
327
|
+
const elapsed = now - this.lastSubmit;
|
|
328
|
+
|
|
329
|
+
if (elapsed < this.minDelayMs) {
|
|
330
|
+
await this.sleep(this.minDelayMs - elapsed);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
this.lastSubmit = Date.now();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private sleep(ms: number): Promise<void> {
|
|
337
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ===== Metrics Collector =====
|
|
342
|
+
|
|
343
|
+
export class MetricsCollector {
|
|
344
|
+
private totalTrades: number = 0;
|
|
345
|
+
private successTrades: number = 0;
|
|
346
|
+
private failedTrades: number = 0;
|
|
347
|
+
private totalLatency: number = 0;
|
|
348
|
+
|
|
349
|
+
recordTrade(success: boolean, latencyMs: number): void {
|
|
350
|
+
this.totalTrades++;
|
|
351
|
+
if (success) {
|
|
352
|
+
this.successTrades++;
|
|
353
|
+
} else {
|
|
354
|
+
this.failedTrades++;
|
|
355
|
+
}
|
|
356
|
+
this.totalLatency += latencyMs;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
getStats(): { total: number; success: number; failed: number; avgLatencyMs: number } {
|
|
360
|
+
return {
|
|
361
|
+
total: this.totalTrades,
|
|
362
|
+
success: this.successTrades,
|
|
363
|
+
failed: this.failedTrades,
|
|
364
|
+
avgLatencyMs: this.totalTrades > 0 ? this.totalLatency / this.totalTrades : 0,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ===== Convenience Functions =====
|
|
370
|
+
|
|
371
|
+
export function createTradeExecutor(
|
|
372
|
+
rpcUrl: string,
|
|
373
|
+
swqosTypes: SwqosType[],
|
|
374
|
+
apiKeys?: Map<SwqosType, string>
|
|
375
|
+
): TradeExecutor {
|
|
376
|
+
const swqosConfigs: SwqosClientConfig[] = swqosTypes.map(type => ({
|
|
377
|
+
type,
|
|
378
|
+
apiKey: apiKeys?.get(type),
|
|
379
|
+
}));
|
|
380
|
+
|
|
381
|
+
return new TradeExecutor({
|
|
382
|
+
rpcUrl,
|
|
383
|
+
swqosConfigs,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trading factory and executor for Sol Trade SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides factory methods for creating trade executors for different DEX protocols
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PublicKey } from '@solana/web3.js';
|
|
8
|
+
|
|
9
|
+
// ===== DEX Types =====
|
|
10
|
+
|
|
11
|
+
export enum DexType {
|
|
12
|
+
PumpFun = 'PumpFun',
|
|
13
|
+
PumpSwap = 'PumpSwap',
|
|
14
|
+
Bonk = 'Bonk',
|
|
15
|
+
RaydiumCpmm = 'RaydiumCpmm',
|
|
16
|
+
RaydiumAmmV4 = 'RaydiumAmmV4',
|
|
17
|
+
MeteoraDammV2 = 'MeteoraDammV2',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export enum TradeType {
|
|
21
|
+
Buy = 'Buy',
|
|
22
|
+
Sell = 'Sell',
|
|
23
|
+
Create = 'Create',
|
|
24
|
+
CreateAndBuy = 'CreateAndBuy',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ===== Trade Result Types =====
|
|
28
|
+
|
|
29
|
+
export interface TradeResult {
|
|
30
|
+
signature: string;
|
|
31
|
+
success: boolean;
|
|
32
|
+
error?: string;
|
|
33
|
+
confirmationTimeMs?: number;
|
|
34
|
+
submittedAt?: Date;
|
|
35
|
+
confirmedAt?: Date;
|
|
36
|
+
retries?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface BatchTradeResult {
|
|
40
|
+
results: TradeResult[];
|
|
41
|
+
totalTimeMs: number;
|
|
42
|
+
successCount: number;
|
|
43
|
+
failedCount: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ===== Execute Options =====
|
|
47
|
+
|
|
48
|
+
export interface TradeExecuteOptions {
|
|
49
|
+
waitConfirmation?: boolean;
|
|
50
|
+
maxRetries?: number;
|
|
51
|
+
retryDelayMs?: number;
|
|
52
|
+
parallelSubmit?: boolean;
|
|
53
|
+
timeoutMs?: number;
|
|
54
|
+
priority?: number;
|
|
55
|
+
skipPreflight?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function defaultTradeExecuteOptions(): TradeExecuteOptions {
|
|
59
|
+
return {
|
|
60
|
+
waitConfirmation: true,
|
|
61
|
+
maxRetries: 3,
|
|
62
|
+
retryDelayMs: 100,
|
|
63
|
+
parallelSubmit: true,
|
|
64
|
+
timeoutMs: 30000,
|
|
65
|
+
priority: 0,
|
|
66
|
+
skipPreflight: false,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ===== Protocol Params =====
|
|
71
|
+
|
|
72
|
+
export interface PumpFunParams {
|
|
73
|
+
bondingCurve?: any;
|
|
74
|
+
associatedBondingCurve?: PublicKey;
|
|
75
|
+
creatorVault?: PublicKey;
|
|
76
|
+
tokenProgram?: PublicKey;
|
|
77
|
+
closeTokenAccountWhenSell?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface PumpSwapParams {
|
|
81
|
+
pool?: PublicKey;
|
|
82
|
+
baseMint?: PublicKey;
|
|
83
|
+
quoteMint?: PublicKey;
|
|
84
|
+
poolBaseTokenAccount?: PublicKey;
|
|
85
|
+
poolQuoteTokenAccount?: PublicKey;
|
|
86
|
+
poolBaseTokenReserves?: bigint;
|
|
87
|
+
poolQuoteTokenReserves?: bigint;
|
|
88
|
+
coinCreatorVaultAta?: PublicKey;
|
|
89
|
+
coinCreatorVaultAuthority?: PublicKey;
|
|
90
|
+
baseTokenProgram?: PublicKey;
|
|
91
|
+
quoteTokenProgram?: PublicKey;
|
|
92
|
+
isMayhemMode?: boolean;
|
|
93
|
+
isCashbackCoin?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface BonkParams {
|
|
97
|
+
virtualBase?: bigint;
|
|
98
|
+
virtualQuote?: bigint;
|
|
99
|
+
realBase?: bigint;
|
|
100
|
+
realQuote?: bigint;
|
|
101
|
+
poolState?: PublicKey;
|
|
102
|
+
baseVault?: PublicKey;
|
|
103
|
+
quoteVault?: PublicKey;
|
|
104
|
+
mintTokenProgram?: PublicKey;
|
|
105
|
+
platformConfig?: PublicKey;
|
|
106
|
+
platformAssociatedAccount?: PublicKey;
|
|
107
|
+
creatorAssociatedAccount?: PublicKey;
|
|
108
|
+
globalConfig?: PublicKey;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface RaydiumCpmmParams {
|
|
112
|
+
poolState?: PublicKey;
|
|
113
|
+
ammConfig?: PublicKey;
|
|
114
|
+
baseMint?: PublicKey;
|
|
115
|
+
quoteMint?: PublicKey;
|
|
116
|
+
baseReserve?: bigint;
|
|
117
|
+
quoteReserve?: bigint;
|
|
118
|
+
baseVault?: PublicKey;
|
|
119
|
+
quoteVault?: PublicKey;
|
|
120
|
+
baseTokenProgram?: PublicKey;
|
|
121
|
+
quoteTokenProgram?: PublicKey;
|
|
122
|
+
observationState?: PublicKey;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface RaydiumAmmV4Params {
|
|
126
|
+
amm?: PublicKey;
|
|
127
|
+
coinMint?: PublicKey;
|
|
128
|
+
pcMint?: PublicKey;
|
|
129
|
+
tokenCoin?: PublicKey;
|
|
130
|
+
tokenPc?: PublicKey;
|
|
131
|
+
coinReserve?: bigint;
|
|
132
|
+
pcReserve?: bigint;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface MeteoraDammV2Params {
|
|
136
|
+
pool?: PublicKey;
|
|
137
|
+
tokenAVault?: PublicKey;
|
|
138
|
+
tokenBVault?: PublicKey;
|
|
139
|
+
tokenAMint?: PublicKey;
|
|
140
|
+
tokenBMint?: PublicKey;
|
|
141
|
+
tokenAProgram?: PublicKey;
|
|
142
|
+
tokenBProgram?: PublicKey;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ===== Trade Executor Interface =====
|
|
146
|
+
|
|
147
|
+
export interface ITradeExecutor {
|
|
148
|
+
executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult>;
|
|
149
|
+
executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ===== Base Executor =====
|
|
153
|
+
|
|
154
|
+
export abstract class BaseExecutor implements ITradeExecutor {
|
|
155
|
+
abstract executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult>;
|
|
156
|
+
abstract executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult>;
|
|
157
|
+
|
|
158
|
+
protected buildResult(
|
|
159
|
+
signature: string,
|
|
160
|
+
success: boolean,
|
|
161
|
+
error?: string
|
|
162
|
+
): TradeResult {
|
|
163
|
+
return {
|
|
164
|
+
signature,
|
|
165
|
+
success,
|
|
166
|
+
error,
|
|
167
|
+
submittedAt: new Date(),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ===== PumpFun Executor =====
|
|
173
|
+
|
|
174
|
+
export class PumpFunExecutor extends BaseExecutor {
|
|
175
|
+
async executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
176
|
+
return this.buildResult('', true);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
180
|
+
return this.buildResult('', true);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ===== PumpSwap Executor =====
|
|
185
|
+
|
|
186
|
+
export class PumpSwapExecutor extends BaseExecutor {
|
|
187
|
+
async executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
188
|
+
return this.buildResult('', true);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
192
|
+
return this.buildResult('', true);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ===== Bonk Executor =====
|
|
197
|
+
|
|
198
|
+
export class BonkExecutor extends BaseExecutor {
|
|
199
|
+
async executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
200
|
+
return this.buildResult('', true);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
204
|
+
return this.buildResult('', true);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ===== Raydium CPMM Executor =====
|
|
209
|
+
|
|
210
|
+
export class RaydiumCpmmExecutor extends BaseExecutor {
|
|
211
|
+
async executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
212
|
+
return this.buildResult('', true);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
216
|
+
return this.buildResult('', true);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ===== Raydium AMM V4 Executor =====
|
|
221
|
+
|
|
222
|
+
export class RaydiumAmmV4Executor extends BaseExecutor {
|
|
223
|
+
async executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
224
|
+
return this.buildResult('', true);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
228
|
+
return this.buildResult('', true);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ===== Meteora DAMM V2 Executor =====
|
|
233
|
+
|
|
234
|
+
export class MeteoraDammV2Executor extends BaseExecutor {
|
|
235
|
+
async executeBuy(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
236
|
+
return this.buildResult('', true);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async executeSell(_params: Record<string, unknown>, _opts?: TradeExecuteOptions): Promise<TradeResult> {
|
|
240
|
+
return this.buildResult('', true);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ===== Trade Executor Factory =====
|
|
245
|
+
|
|
246
|
+
export class TradeExecutorFactory {
|
|
247
|
+
private static executors: Map<DexType, () => ITradeExecutor> = new Map([
|
|
248
|
+
[DexType.PumpFun, () => new PumpFunExecutor()],
|
|
249
|
+
[DexType.PumpSwap, () => new PumpSwapExecutor()],
|
|
250
|
+
[DexType.Bonk, () => new BonkExecutor()],
|
|
251
|
+
[DexType.RaydiumCpmm, () => new RaydiumCpmmExecutor()],
|
|
252
|
+
[DexType.RaydiumAmmV4, () => new RaydiumAmmV4Executor()],
|
|
253
|
+
[DexType.MeteoraDammV2, () => new MeteoraDammV2Executor()],
|
|
254
|
+
]);
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Create a trade executor for the given DEX type
|
|
258
|
+
*/
|
|
259
|
+
static createExecutor(dexType: DexType): ITradeExecutor {
|
|
260
|
+
const factory = this.executors.get(dexType);
|
|
261
|
+
if (!factory) {
|
|
262
|
+
throw new Error(`No executor available for DEX type: ${dexType}`);
|
|
263
|
+
}
|
|
264
|
+
return factory();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Register a custom executor factory
|
|
269
|
+
*/
|
|
270
|
+
static registerExecutor(dexType: DexType, factory: () => ITradeExecutor): void {
|
|
271
|
+
this.executors.set(dexType, factory);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Get supported DEX types
|
|
276
|
+
*/
|
|
277
|
+
static getSupportedDexTypes(): DexType[] {
|
|
278
|
+
return Array.from(this.executors.keys());
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Main `TradingClient` lives in `src/index.ts` (parity with Rust SDK). Use `TradeExecutorFactory` for protocol stubs/tests.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trading module exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './executor';
|
|
6
|
+
export * from './core';
|
|
7
|
+
export {
|
|
8
|
+
DexType,
|
|
9
|
+
TradeType as FactoryTradeType,
|
|
10
|
+
defaultTradeExecuteOptions,
|
|
11
|
+
PumpFunExecutor,
|
|
12
|
+
PumpSwapExecutor,
|
|
13
|
+
BonkExecutor,
|
|
14
|
+
RaydiumCpmmExecutor,
|
|
15
|
+
RaydiumAmmV4Executor,
|
|
16
|
+
MeteoraDammV2Executor,
|
|
17
|
+
TradeExecutorFactory,
|
|
18
|
+
} from './factory';
|
|
19
|
+
export type {
|
|
20
|
+
TradeResult as FactoryTradeResult,
|
|
21
|
+
BatchTradeResult,
|
|
22
|
+
TradeExecuteOptions,
|
|
23
|
+
PumpFunParams,
|
|
24
|
+
PumpSwapParams,
|
|
25
|
+
BonkParams,
|
|
26
|
+
RaydiumCpmmParams,
|
|
27
|
+
RaydiumAmmV4Params,
|
|
28
|
+
MeteoraDammV2Params,
|
|
29
|
+
ITradeExecutor,
|
|
30
|
+
} from './factory';
|