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
package/src/pool/pool.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-Performance Pool and Concurrency Utilities for Sol Trade SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// ===== Types =====
|
|
6
|
+
|
|
7
|
+
interface PoolStats {
|
|
8
|
+
activeTasks: number;
|
|
9
|
+
tasksCompleted: number;
|
|
10
|
+
queueSize: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ===== Worker Pool =====
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* High-performance worker pool for parallel task execution.
|
|
17
|
+
*/
|
|
18
|
+
export class WorkerPool {
|
|
19
|
+
private taskQueue: Array<() => Promise<any>> = [];
|
|
20
|
+
private activeWorkers = 0;
|
|
21
|
+
private tasksCompleted = 0;
|
|
22
|
+
private maxWorkers: number;
|
|
23
|
+
private maxQueueSize: number;
|
|
24
|
+
private idleResolvers: Array<() => void> = [];
|
|
25
|
+
|
|
26
|
+
constructor(workers: number = 4, maxQueueSize: number = 100) {
|
|
27
|
+
this.maxWorkers = workers;
|
|
28
|
+
this.maxQueueSize = maxQueueSize;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async submit<T>(task: () => Promise<T>): Promise<T> {
|
|
32
|
+
if (this.taskQueue.length >= this.maxQueueSize) {
|
|
33
|
+
throw new Error('Task queue is full');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
this.taskQueue.push(async () => {
|
|
38
|
+
try {
|
|
39
|
+
const result = await task();
|
|
40
|
+
resolve(result);
|
|
41
|
+
return result;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
reject(error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
this.processQueue();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async submitBatch<T>(tasks: Array<() => Promise<T>>): Promise<T[]> {
|
|
52
|
+
return Promise.all(tasks.map(task => this.submit(task)));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private async processQueue(): Promise<void> {
|
|
56
|
+
while (this.taskQueue.length > 0 && this.activeWorkers < this.maxWorkers) {
|
|
57
|
+
const task = this.taskQueue.shift()!;
|
|
58
|
+
this.activeWorkers++;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
await task();
|
|
62
|
+
} finally {
|
|
63
|
+
this.activeWorkers--;
|
|
64
|
+
this.tasksCompleted++;
|
|
65
|
+
this.processQueue();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (this.activeWorkers === 0 && this.taskQueue.length === 0) {
|
|
70
|
+
this.idleResolvers.forEach(resolve => resolve());
|
|
71
|
+
this.idleResolvers = [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async idle(): Promise<void> {
|
|
76
|
+
if (this.activeWorkers === 0 && this.taskQueue.length === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
return new Promise(resolve => {
|
|
80
|
+
this.idleResolvers.push(resolve);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getStats(): PoolStats {
|
|
85
|
+
return {
|
|
86
|
+
activeTasks: this.activeWorkers,
|
|
87
|
+
tasksCompleted: this.tasksCompleted,
|
|
88
|
+
queueSize: this.taskQueue.length,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ===== Rate Limiter =====
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Token bucket rate limiter.
|
|
97
|
+
*/
|
|
98
|
+
export class RateLimiter {
|
|
99
|
+
private tokens: number;
|
|
100
|
+
private lastUpdate: number;
|
|
101
|
+
|
|
102
|
+
constructor(
|
|
103
|
+
private rate: number,
|
|
104
|
+
private burst: number = 10
|
|
105
|
+
) {
|
|
106
|
+
this.tokens = burst;
|
|
107
|
+
this.lastUpdate = Date.now();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
allow(): boolean {
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
const elapsed = (now - this.lastUpdate) / 1000;
|
|
113
|
+
this.lastUpdate = now;
|
|
114
|
+
|
|
115
|
+
// Add tokens based on elapsed time
|
|
116
|
+
this.tokens += elapsed * this.rate;
|
|
117
|
+
if (this.tokens > this.burst) {
|
|
118
|
+
this.tokens = this.burst;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (this.tokens >= 1) {
|
|
122
|
+
this.tokens--;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async wait(): Promise<void> {
|
|
129
|
+
while (!this.allow()) {
|
|
130
|
+
await new Promise(resolve => setTimeout(resolve, 1));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ===== Multi Rate Limiter =====
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Rate limiter with multiple keys.
|
|
139
|
+
*/
|
|
140
|
+
export class MultiRateLimiter {
|
|
141
|
+
private limiters: Map<string, RateLimiter> = new Map();
|
|
142
|
+
|
|
143
|
+
constructor(
|
|
144
|
+
private rate: number,
|
|
145
|
+
private burst: number = 10
|
|
146
|
+
) {}
|
|
147
|
+
|
|
148
|
+
allow(key: string): boolean {
|
|
149
|
+
let limiter = this.limiters.get(key);
|
|
150
|
+
if (!limiter) {
|
|
151
|
+
limiter = new RateLimiter(this.rate, this.burst);
|
|
152
|
+
this.limiters.set(key, limiter);
|
|
153
|
+
}
|
|
154
|
+
return limiter.allow();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async wait(key: string): Promise<void> {
|
|
158
|
+
while (!this.allow(key)) {
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, 1));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ===== Connection Pool =====
|
|
165
|
+
|
|
166
|
+
export interface Connection {
|
|
167
|
+
close(): void;
|
|
168
|
+
isAlive(): boolean;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Generic connection pool with automatic management.
|
|
173
|
+
*/
|
|
174
|
+
export class ConnectionPool<T extends Connection> {
|
|
175
|
+
private pool: T[] = [];
|
|
176
|
+
private created = 0;
|
|
177
|
+
private waiting = 0;
|
|
178
|
+
|
|
179
|
+
constructor(
|
|
180
|
+
private factory: () => T,
|
|
181
|
+
private maxSize: number = 10,
|
|
182
|
+
private timeout: number = 30000
|
|
183
|
+
) {}
|
|
184
|
+
|
|
185
|
+
async get(): Promise<T> {
|
|
186
|
+
if (this.pool.length > 0) {
|
|
187
|
+
const conn = this.pool.pop()!;
|
|
188
|
+
if (conn.isAlive()) {
|
|
189
|
+
return conn;
|
|
190
|
+
}
|
|
191
|
+
this.created--;
|
|
192
|
+
return this.create();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (this.created < this.maxSize) {
|
|
196
|
+
return this.create();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Wait for available connection
|
|
200
|
+
this.waiting++;
|
|
201
|
+
const startTime = Date.now();
|
|
202
|
+
|
|
203
|
+
while (Date.now() - startTime < this.timeout) {
|
|
204
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
205
|
+
if (this.pool.length > 0) {
|
|
206
|
+
const conn = this.pool.pop()!;
|
|
207
|
+
if (conn.isAlive()) {
|
|
208
|
+
this.waiting--;
|
|
209
|
+
return conn;
|
|
210
|
+
}
|
|
211
|
+
this.created--;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.waiting--;
|
|
216
|
+
throw new Error('Connection timeout');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
release(conn: T): void {
|
|
220
|
+
if (!conn.isAlive()) {
|
|
221
|
+
this.created--;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (this.pool.length < this.maxSize) {
|
|
226
|
+
this.pool.push(conn);
|
|
227
|
+
} else {
|
|
228
|
+
conn.close();
|
|
229
|
+
this.created--;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private create(): T {
|
|
234
|
+
const conn = this.factory();
|
|
235
|
+
this.created++;
|
|
236
|
+
return conn;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
closeAll(): void {
|
|
240
|
+
for (const conn of this.pool) {
|
|
241
|
+
conn.close();
|
|
242
|
+
}
|
|
243
|
+
this.pool = [];
|
|
244
|
+
this.created = 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
getStats(): { created: number; available: number; waiting: number } {
|
|
248
|
+
return {
|
|
249
|
+
created: this.created,
|
|
250
|
+
available: this.pool.length,
|
|
251
|
+
waiting: this.waiting,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ===== Object Pool =====
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Generic object pool for reusing expensive objects.
|
|
260
|
+
*/
|
|
261
|
+
export class ObjectPool<T> {
|
|
262
|
+
private pool: T[] = [];
|
|
263
|
+
|
|
264
|
+
constructor(
|
|
265
|
+
private factory: () => T,
|
|
266
|
+
private resetFunc?: (obj: T) => void,
|
|
267
|
+
private maxSize: number = 100
|
|
268
|
+
) {}
|
|
269
|
+
|
|
270
|
+
acquire(): T {
|
|
271
|
+
if (this.pool.length > 0) {
|
|
272
|
+
return this.pool.pop()!;
|
|
273
|
+
}
|
|
274
|
+
return this.factory();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
release(obj: T): void {
|
|
278
|
+
if (this.resetFunc) {
|
|
279
|
+
this.resetFunc(obj);
|
|
280
|
+
}
|
|
281
|
+
if (this.pool.length < this.maxSize) {
|
|
282
|
+
this.pool.push(obj);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
with<R>(fn: (obj: T) => R): R {
|
|
287
|
+
const obj = this.acquire();
|
|
288
|
+
try {
|
|
289
|
+
return fn(obj);
|
|
290
|
+
} finally {
|
|
291
|
+
this.release(obj);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-Performance RPC Client for Sol Trade SDK
|
|
3
|
+
* Provides async methods for Solana RPC communication.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { TTLCache, ShardedCache } from '../cache/cache';
|
|
7
|
+
|
|
8
|
+
// ===== Types =====
|
|
9
|
+
|
|
10
|
+
interface RPCConfig {
|
|
11
|
+
endpoint: string;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
maxRetries?: number;
|
|
14
|
+
retryDelay?: number;
|
|
15
|
+
headers?: Record<string, string>;
|
|
16
|
+
maxConnections?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface AccountInfo {
|
|
20
|
+
lamports: number;
|
|
21
|
+
data: Buffer;
|
|
22
|
+
owner: string;
|
|
23
|
+
executable: boolean;
|
|
24
|
+
rentEpoch: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface BlockhashResult {
|
|
28
|
+
blockhash: string;
|
|
29
|
+
lastValidBlockHeight: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface SignatureStatus {
|
|
33
|
+
slot: number;
|
|
34
|
+
confirmations?: number;
|
|
35
|
+
err?: any;
|
|
36
|
+
confirmationStatus: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface SimulateResult {
|
|
40
|
+
err?: any;
|
|
41
|
+
accounts?: AccountInfo[];
|
|
42
|
+
unitsConsumed: number;
|
|
43
|
+
returnData?: {
|
|
44
|
+
data: Buffer;
|
|
45
|
+
programId: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface JsonRpcResponse<T> {
|
|
50
|
+
result?: T;
|
|
51
|
+
error?: {
|
|
52
|
+
code?: number;
|
|
53
|
+
message?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ===== RPC Error =====
|
|
58
|
+
|
|
59
|
+
export class RPCError extends Error {
|
|
60
|
+
constructor(public code: number, message: string) {
|
|
61
|
+
super(`RPC Error ${code}: ${message}`);
|
|
62
|
+
this.name = 'RPCError';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ===== Async RPC Client =====
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* High-performance async RPC client.
|
|
70
|
+
*/
|
|
71
|
+
export class AsyncRPCClient {
|
|
72
|
+
private requestId = 0;
|
|
73
|
+
private requestsCount = 0;
|
|
74
|
+
private errorCount = 0;
|
|
75
|
+
private totalLatency = 0;
|
|
76
|
+
|
|
77
|
+
// Caches
|
|
78
|
+
private blockhashCache = new TTLCache<string, string>(2000);
|
|
79
|
+
private accountCache = new ShardedCache<string, Buffer>(16, 500, 10000);
|
|
80
|
+
|
|
81
|
+
constructor(private config: RPCConfig) {}
|
|
82
|
+
|
|
83
|
+
private async makeRequest<T>(method: string, params: any[] = []): Promise<T> {
|
|
84
|
+
this.requestId++;
|
|
85
|
+
this.requestsCount++;
|
|
86
|
+
|
|
87
|
+
const payload = {
|
|
88
|
+
jsonrpc: '2.0',
|
|
89
|
+
id: this.requestId,
|
|
90
|
+
method,
|
|
91
|
+
params,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const startTime = Date.now();
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const controller = new AbortController();
|
|
98
|
+
const timeout = setTimeout(() => controller.abort(), this.config.timeout || 30000);
|
|
99
|
+
|
|
100
|
+
const response = await fetch(this.config.endpoint, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: {
|
|
103
|
+
'Content-Type': 'application/json',
|
|
104
|
+
...this.config.headers,
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify(payload),
|
|
107
|
+
signal: controller.signal,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
|
|
112
|
+
const data = await response.json() as JsonRpcResponse<T>;
|
|
113
|
+
|
|
114
|
+
if (data.error) {
|
|
115
|
+
this.errorCount++;
|
|
116
|
+
throw new RPCError(
|
|
117
|
+
data.error.code || -1,
|
|
118
|
+
data.error.message || 'Unknown error'
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return data.result as T;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
this.errorCount++;
|
|
125
|
+
if (error instanceof RPCError) throw error;
|
|
126
|
+
throw new RPCError(-1, error instanceof Error ? error.message : 'Unknown error');
|
|
127
|
+
} finally {
|
|
128
|
+
this.totalLatency += Date.now() - startTime;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ===== Core Methods =====
|
|
133
|
+
|
|
134
|
+
async getBalance(pubkey: string, commitment: string = 'confirmed'): Promise<number> {
|
|
135
|
+
const result = await this.makeRequest<{ value: number }>('getBalance', [
|
|
136
|
+
pubkey,
|
|
137
|
+
{ commitment },
|
|
138
|
+
]);
|
|
139
|
+
return result.value;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async getAccountInfo(
|
|
143
|
+
pubkey: string,
|
|
144
|
+
encoding: string = 'base64',
|
|
145
|
+
commitment: string = 'confirmed'
|
|
146
|
+
): Promise<AccountInfo | null> {
|
|
147
|
+
// Check cache
|
|
148
|
+
const cached = this.accountCache.get(pubkey);
|
|
149
|
+
if (cached) {
|
|
150
|
+
return {
|
|
151
|
+
lamports: 0,
|
|
152
|
+
data: cached,
|
|
153
|
+
owner: '',
|
|
154
|
+
executable: false,
|
|
155
|
+
rentEpoch: 0,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const result = await this.makeRequest<{ value: any }>('getAccountInfo', [
|
|
160
|
+
pubkey,
|
|
161
|
+
{ encoding, commitment },
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
if (!result.value) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const data = result.value.data;
|
|
169
|
+
let rawData: Buffer;
|
|
170
|
+
|
|
171
|
+
if (Array.isArray(data) && data[0]) {
|
|
172
|
+
rawData = Buffer.from(data[0], 'base64');
|
|
173
|
+
} else {
|
|
174
|
+
rawData = Buffer.alloc(0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.accountCache.set(pubkey, rawData);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
lamports: result.value.lamports || 0,
|
|
181
|
+
data: rawData,
|
|
182
|
+
owner: result.value.owner || '',
|
|
183
|
+
executable: result.value.executable || false,
|
|
184
|
+
rentEpoch: result.value.rentEpoch || 0,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async getMultipleAccounts(
|
|
189
|
+
pubkeys: string[],
|
|
190
|
+
encoding: string = 'base64',
|
|
191
|
+
commitment: string = 'confirmed'
|
|
192
|
+
): Promise<(AccountInfo | null)[]> {
|
|
193
|
+
const result = await this.makeRequest<{ value: any[] }>('getMultipleAccounts', [
|
|
194
|
+
pubkeys,
|
|
195
|
+
{ encoding, commitment },
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
return (result.value || []).map((value: any) => {
|
|
199
|
+
if (!value) return null;
|
|
200
|
+
|
|
201
|
+
const data = value.data;
|
|
202
|
+
let rawData: Buffer;
|
|
203
|
+
|
|
204
|
+
if (Array.isArray(data) && data[0]) {
|
|
205
|
+
rawData = Buffer.from(data[0], 'base64');
|
|
206
|
+
} else {
|
|
207
|
+
rawData = Buffer.alloc(0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
lamports: value.lamports || 0,
|
|
212
|
+
data: rawData,
|
|
213
|
+
owner: value.owner || '',
|
|
214
|
+
executable: value.executable || false,
|
|
215
|
+
rentEpoch: value.rentEpoch || 0,
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async getLatestBlockhash(commitment: string = 'confirmed'): Promise<BlockhashResult> {
|
|
221
|
+
// Check cache
|
|
222
|
+
const cached = this.blockhashCache.get('latest');
|
|
223
|
+
if (cached) {
|
|
224
|
+
return {
|
|
225
|
+
blockhash: cached,
|
|
226
|
+
lastValidBlockHeight: 0,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const result = await this.makeRequest<BlockhashResult>('getLatestBlockhash', [
|
|
231
|
+
{ commitment },
|
|
232
|
+
]);
|
|
233
|
+
|
|
234
|
+
this.blockhashCache.set('latest', result.blockhash);
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
blockhash: result.blockhash,
|
|
238
|
+
lastValidBlockHeight: result.lastValidBlockHeight,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async getSignatureStatuses(
|
|
243
|
+
signatures: string[],
|
|
244
|
+
searchTransactionHistory: boolean = false
|
|
245
|
+
): Promise<(SignatureStatus | null)[]> {
|
|
246
|
+
const result = await this.makeRequest<{ value: any[] }>('getSignatureStatuses', [
|
|
247
|
+
signatures,
|
|
248
|
+
{ searchTransactionHistory },
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
return (result.value || []).map((value: any) => {
|
|
252
|
+
if (!value) return null;
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
slot: value.slot || 0,
|
|
256
|
+
confirmations: value.confirmations,
|
|
257
|
+
err: value.err,
|
|
258
|
+
confirmationStatus: value.confirmationStatus || 'processed',
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async sendTransaction(
|
|
264
|
+
transaction: Buffer,
|
|
265
|
+
options: {
|
|
266
|
+
skipPreflight?: boolean;
|
|
267
|
+
preflightCommitment?: string;
|
|
268
|
+
} = {}
|
|
269
|
+
): Promise<string> {
|
|
270
|
+
const encoded = transaction.toString('base64');
|
|
271
|
+
|
|
272
|
+
return this.makeRequest<string>('sendTransaction', [
|
|
273
|
+
encoded,
|
|
274
|
+
{
|
|
275
|
+
encoding: 'base64',
|
|
276
|
+
skipPreflight: options.skipPreflight || false,
|
|
277
|
+
preflightCommitment: options.preflightCommitment || 'confirmed',
|
|
278
|
+
},
|
|
279
|
+
]);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async simulateTransaction(
|
|
283
|
+
transaction: Buffer,
|
|
284
|
+
options: {
|
|
285
|
+
sigVerify?: boolean;
|
|
286
|
+
} = {}
|
|
287
|
+
): Promise<SimulateResult> {
|
|
288
|
+
const encoded = transaction.toString('base64');
|
|
289
|
+
|
|
290
|
+
const result = await this.makeRequest<{ value: any }>('simulateTransaction', [
|
|
291
|
+
encoded,
|
|
292
|
+
{
|
|
293
|
+
encoding: 'base64',
|
|
294
|
+
sigVerify: options.sigVerify || false,
|
|
295
|
+
},
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
err: result.value?.err,
|
|
300
|
+
accounts: result.value?.accounts,
|
|
301
|
+
unitsConsumed: result.value?.unitsConsumed || 0,
|
|
302
|
+
returnData: result.value?.returnData,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ===== Utility Methods =====
|
|
307
|
+
|
|
308
|
+
/** Rust `poll_any_transaction_confirmation`: success when err is none and status is confirmed or finalized. */
|
|
309
|
+
async waitForConfirmation(
|
|
310
|
+
signature: string,
|
|
311
|
+
timeout: number = 30000,
|
|
312
|
+
pollInterval: number = 1000
|
|
313
|
+
): Promise<boolean> {
|
|
314
|
+
const startTime = Date.now();
|
|
315
|
+
|
|
316
|
+
while (Date.now() - startTime < timeout) {
|
|
317
|
+
const statuses = await this.getSignatureStatuses([signature]);
|
|
318
|
+
const status = statuses[0];
|
|
319
|
+
|
|
320
|
+
if (status) {
|
|
321
|
+
if (status.err) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
const cs = status.confirmationStatus;
|
|
325
|
+
if (cs === 'confirmed' || cs === 'finalized') {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
getStats(): { requests: number; errors: number; avgLatencyMs: number } {
|
|
337
|
+
return {
|
|
338
|
+
requests: this.requestsCount,
|
|
339
|
+
errors: this.errorCount,
|
|
340
|
+
avgLatencyMs: this.requestsCount > 0
|
|
341
|
+
? this.totalLatency / this.requestsCount
|
|
342
|
+
: 0,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared SDK errors (avoids circular imports with `index` / `swqos`).
|
|
3
|
+
*/
|
|
4
|
+
export class TradeError extends Error {
|
|
5
|
+
constructor(
|
|
6
|
+
public code: number,
|
|
7
|
+
message: string,
|
|
8
|
+
public cause?: Error
|
|
9
|
+
) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'TradeError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security module for Sol Trade SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides secure key storage and input validation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
SecureKeyStorage,
|
|
9
|
+
SecureKeyError,
|
|
10
|
+
KeyNotAvailableError,
|
|
11
|
+
signWithKeypair,
|
|
12
|
+
type KeyMetadata,
|
|
13
|
+
} from './secure-key';
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
ValidationError,
|
|
17
|
+
KNOWN_PROGRAM_IDS,
|
|
18
|
+
validateRpcUrl,
|
|
19
|
+
validateProgramId,
|
|
20
|
+
validateAmount,
|
|
21
|
+
validateSlippage,
|
|
22
|
+
validatePubkey,
|
|
23
|
+
validateMintPair,
|
|
24
|
+
validateTransactionSize,
|
|
25
|
+
validateSignature,
|
|
26
|
+
} from './validators';
|