solana-tx-kit 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 solana-tx-kit contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,369 @@
1
+ # solana-tx-kit
2
+
3
+ [![npm version](https://img.shields.io/npm/v/solana-tx-kit.svg)](https://www.npmjs.com/package/solana-tx-kit)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org)
6
+
7
+ Production-grade Solana transaction infrastructure with retry logic, priority fees, RPC pooling, Jito bundles, and full lifecycle events.
8
+
9
+ ## Features
10
+
11
+ - **Builder Pattern API** -- configure and compose exactly what you need
12
+ - **Exponential Backoff Retry** -- full-jitter backoff with error classification (retryable, non-retryable, blockhash-expired)
13
+ - **Dynamic Priority Fees** -- percentile-based estimation from recent fees, automatic compute budget instructions
14
+ - **RPC Connection Pool** -- multi-endpoint failover, weighted round-robin or latency-based selection, circuit breaker per endpoint, EMA health tracking
15
+ - **Blockhash Management** -- background refresh, TTL cache, promise coalescing
16
+ - **Transaction Simulation** -- pre-flight checks, compute unit extraction, error decoding
17
+ - **Transaction Confirmation** -- WebSocket + polling dual strategy, block height expiry detection
18
+ - **Jito Bundle Support** -- 1-5 transaction bundles, tip account rotation, status polling
19
+ - **Typed Lifecycle Events** -- SENDING, SENT, SIMULATED, CONFIRMED, RETRYING, BLOCKHASH_EXPIRED, FAILED, BUNDLE_*
20
+ - **Standalone Modules** -- every module is usable independently
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pnpm add solana-tx-kit @solana/web3.js
26
+ ```
27
+
28
+ ```bash
29
+ npm install solana-tx-kit @solana/web3.js
30
+ ```
31
+
32
+ ```bash
33
+ yarn add solana-tx-kit @solana/web3.js
34
+ ```
35
+
36
+ > **Peer dependency:** `@solana/web3.js ^1.87.0`
37
+
38
+ ## Quick Start
39
+
40
+ ```typescript
41
+ import { TransactionSender } from "solana-tx-kit";
42
+ import { Keypair, Transaction, SystemProgram, PublicKey } from "@solana/web3.js";
43
+
44
+ const sender = TransactionSender.builder()
45
+ .rpc("https://api.mainnet-beta.solana.com")
46
+ .signer(keypair)
47
+ .withPriorityFees({ targetPercentile: 75 })
48
+ .withRetry({ maxRetries: 5 })
49
+ .build();
50
+
51
+ const tx = new Transaction().add(
52
+ SystemProgram.transfer({
53
+ fromPubkey: keypair.publicKey,
54
+ toPubkey: new PublicKey("..."),
55
+ lamports: 1_000_000,
56
+ })
57
+ );
58
+
59
+ const result = await sender.send(tx);
60
+ console.log("Signature:", result.signature);
61
+ ```
62
+
63
+ ## Builder API Reference
64
+
65
+ Chain methods on `TransactionSender.builder()` and call `.build()` to create a sender.
66
+
67
+ ```typescript
68
+ const sender = TransactionSender.builder()
69
+ .rpc("https://api.mainnet-beta.solana.com") // single RPC endpoint
70
+ // .rpcPool([...]) // or multi-endpoint pool
71
+ .signer(keypair) // transaction signer
72
+ .withPriorityFees({ targetPercentile: 75 }) // dynamic fee estimation
73
+ .withRetry({ maxRetries: 5 }) // retry with backoff
74
+ .withJito({ blockEngineUrl: "...", tipPayer: keypair }) // Jito bundles
75
+ .withSimulation({ commitment: "confirmed" }) // pre-flight simulation
76
+ .withConfirmation({ timeoutMs: 30000 }) // confirmation strategy
77
+ .withBlockhash({ ttlMs: 60000 }) // blockhash cache config
78
+ .withLogger(myLogger) // custom logger
79
+ .commitment("confirmed") // default commitment
80
+ .build();
81
+ ```
82
+
83
+ | Method | Description | Required |
84
+ |---|---|---|
85
+ | `.rpc(url)` | Single RPC endpoint | Yes (or `.rpcPool`) |
86
+ | `.rpcPool(endpoints)` | Multi-endpoint connection pool with failover | Yes (or `.rpc`) |
87
+ | `.signer(keypair)` | Transaction signer | Yes |
88
+ | `.withPriorityFees(config)` | Enable dynamic priority fee estimation. Pass `false` to disable. | No |
89
+ | `.withRetry(config)` | Configure retry behavior with exponential backoff | No |
90
+ | `.withJito(config)` | Enable Jito bundle support | No |
91
+ | `.withSimulation(config)` | Configure pre-flight simulation. Pass `false` to disable. | No |
92
+ | `.withConfirmation(config)` | Configure confirmation strategy and timeout | No |
93
+ | `.withBlockhash(config)` | Configure blockhash caching and refresh | No |
94
+ | `.withLogger(logger)` | Provide a logger (`{ debug, info, warn, error }`) | No |
95
+ | `.commitment(level)` | Set default commitment level | No |
96
+
97
+ ## Sending Transactions
98
+
99
+ ### Basic Send
100
+
101
+ ```typescript
102
+ const result = await sender.send(transaction);
103
+ // result: { signature: string }
104
+ ```
105
+
106
+ ### Send with Options
107
+
108
+ ```typescript
109
+ const result = await sender.send(transaction, {
110
+ commitment: "finalized",
111
+ skipPreflight: true,
112
+ });
113
+ ```
114
+
115
+ ### Multiple Transactions
116
+
117
+ ```typescript
118
+ const transactions = [tx1, tx2, tx3];
119
+ const results = await Promise.all(transactions.map((tx) => sender.send(tx)));
120
+ ```
121
+
122
+ ## Jito Bundles
123
+
124
+ Send up to 5 transactions as an atomic bundle through Jito's block engine.
125
+
126
+ ```typescript
127
+ import { TransactionSender } from "solana-tx-kit";
128
+
129
+ const sender = TransactionSender.builder()
130
+ .rpc("https://api.mainnet-beta.solana.com")
131
+ .signer(keypair)
132
+ .withJito({
133
+ blockEngineUrl: "https://mainnet.block-engine.jito.wtf",
134
+ tipPayer: keypair,
135
+ tipLamports: 10_000,
136
+ })
137
+ .build();
138
+
139
+ const bundleResult = await sender.sendBundle([tx1, tx2, tx3]);
140
+ console.log("Bundle ID:", bundleResult.bundleId);
141
+ console.log("Status:", bundleResult.status); // BundleStatus.Landed
142
+ ```
143
+
144
+ ### Standalone Jito Usage
145
+
146
+ ```typescript
147
+ import {
148
+ JitoBundleSender,
149
+ createTipInstruction,
150
+ getNextTipAccount,
151
+ } from "solana-tx-kit";
152
+
153
+ const tipIx = createTipInstruction(payer.publicKey, 10_000);
154
+ transaction.add(tipIx);
155
+
156
+ const bundleSender = new JitoBundleSender({
157
+ blockEngineUrl: "https://mainnet.block-engine.jito.wtf",
158
+ });
159
+ const result = await bundleSender.sendBundle([signedTx]);
160
+ ```
161
+
162
+ ## RPC Connection Pool
163
+
164
+ Distribute requests across multiple RPC endpoints with automatic failover.
165
+
166
+ ```typescript
167
+ import { ConnectionPool } from "solana-tx-kit";
168
+
169
+ const pool = new ConnectionPool({
170
+ endpoints: [
171
+ { url: "https://rpc-1.example.com", weight: 3 },
172
+ { url: "https://rpc-2.example.com", weight: 1 },
173
+ ],
174
+ strategy: "weighted-round-robin", // or "latency-based"
175
+ circuitBreaker: {
176
+ failureThreshold: 5,
177
+ resetTimeMs: 30_000,
178
+ },
179
+ });
180
+
181
+ const connection = pool.getConnection();
182
+ ```
183
+
184
+ Each endpoint has its own circuit breaker and health tracking with EMA latency metrics. Unhealthy endpoints are automatically removed from rotation and re-tested after the reset window.
185
+
186
+ ## Event Listening
187
+
188
+ Subscribe to typed lifecycle events for observability, logging, or custom logic.
189
+
190
+ ```typescript
191
+ sender.on(TxEvent.SENDING, ({ transaction }) => {
192
+ console.log("Sending transaction...");
193
+ });
194
+
195
+ sender.on(TxEvent.SIMULATED, ({ unitsConsumed }) => {
196
+ console.log(`Simulation used ${unitsConsumed} CUs`);
197
+ });
198
+
199
+ sender.on(TxEvent.CONFIRMED, ({ signature, slot }) => {
200
+ console.log(`Confirmed in slot ${slot}: ${signature}`);
201
+ });
202
+
203
+ sender.on(TxEvent.RETRYING, ({ attempt, error }) => {
204
+ console.warn(`Retry #${attempt}: ${error.message}`);
205
+ });
206
+
207
+ sender.on(TxEvent.BLOCKHASH_EXPIRED, ({ signature }) => {
208
+ console.warn(`Blockhash expired for ${signature}, fetching new one...`);
209
+ });
210
+
211
+ sender.on(TxEvent.FAILED, ({ error }) => {
212
+ console.error("Transaction failed:", error);
213
+ });
214
+ ```
215
+
216
+ **All events:** `SENDING`, `SENT`, `SIMULATED`, `CONFIRMED`, `RETRYING`, `BLOCKHASH_EXPIRED`, `FAILED`, `BUNDLE_SENT`, `BUNDLE_CONFIRMED`, `BUNDLE_FAILED`
217
+
218
+ ## Standalone Modules
219
+
220
+ Every module can be used independently without the builder.
221
+
222
+ ```typescript
223
+ import {
224
+ estimatePriorityFee,
225
+ createComputeBudgetInstructions,
226
+ simulateTransaction,
227
+ withRetry,
228
+ classifyError,
229
+ BlockhashManager,
230
+ TransactionConfirmer,
231
+ ConnectionPool,
232
+ } from "solana-tx-kit";
233
+
234
+ // Estimate priority fees from recent transactions
235
+ const fee = await estimatePriorityFee(connection, transaction, {
236
+ targetPercentile: 75,
237
+ });
238
+
239
+ // Create compute budget instructions
240
+ const ixs = createComputeBudgetInstructions({
241
+ computeUnitLimit: 200_000,
242
+ computeUnitPrice: fee.microLamports,
243
+ });
244
+
245
+ // Simulate a transaction
246
+ const sim = await simulateTransaction(connection, transaction, {
247
+ commitment: "confirmed",
248
+ });
249
+
250
+ // Retry any async operation with backoff
251
+ const result = await withRetry(() => fetchData(), { maxRetries: 3 });
252
+
253
+ // Classify errors for retry decisions
254
+ const classification = classifyError(error);
255
+ // => { retryable: true, reason: "rate-limited" }
256
+
257
+ // Manage blockhashes with background refresh
258
+ const bhManager = new BlockhashManager(connection, { ttlMs: 60_000 });
259
+ const { blockhash, lastValidBlockHeight } = await bhManager.getBlockhash();
260
+
261
+ // Confirm transactions with WebSocket + polling
262
+ const confirmer = new TransactionConfirmer(connection);
263
+ const confirmation = await confirmer.confirm(signature, {
264
+ timeoutMs: 30_000,
265
+ });
266
+ ```
267
+
268
+ ## Error Handling
269
+
270
+ All errors are thrown as `SolTxError` with a typed `code` field.
271
+
272
+ ```typescript
273
+ import { SolTxError, SolTxErrorCode, RetryableError } from "solana-tx-kit";
274
+
275
+ try {
276
+ await sender.send(tx);
277
+ } catch (err) {
278
+ if (err instanceof SolTxError) {
279
+ switch (err.code) {
280
+ case SolTxErrorCode.BLOCKHASH_EXPIRED:
281
+ // Transaction expired, rebuild and retry
282
+ break;
283
+ case SolTxErrorCode.INSUFFICIENT_FUNDS:
284
+ // Not enough SOL
285
+ break;
286
+ case SolTxErrorCode.SIMULATION_FAILED:
287
+ // Pre-flight simulation failed
288
+ break;
289
+ case SolTxErrorCode.CONFIRMATION_TIMEOUT:
290
+ // Transaction may have landed but wasn't confirmed in time
291
+ break;
292
+ case SolTxErrorCode.ALL_ENDPOINTS_UNHEALTHY:
293
+ // Every RPC endpoint is in circuit-breaker open state
294
+ break;
295
+ case SolTxErrorCode.RETRIES_EXHAUSTED:
296
+ // All retry attempts failed
297
+ break;
298
+ }
299
+ }
300
+ }
301
+ ```
302
+
303
+ **Error codes:** `RETRIES_EXHAUSTED`, `NON_RETRYABLE`, `BLOCKHASH_EXPIRED`, `BLOCKHASH_FETCH_FAILED`, `SIMULATION_FAILED`, `INSUFFICIENT_FUNDS`, `CONFIRMATION_TIMEOUT`, `TRANSACTION_FAILED`, `ALL_ENDPOINTS_UNHEALTHY`, `RATE_LIMITED`, `BUNDLE_FAILED`, `BUNDLE_DROPPED`, `TIP_TOO_LOW`, `FEE_ESTIMATION_FAILED`
304
+
305
+ ## Configuration Defaults
306
+
307
+ | Module | Setting | Default |
308
+ |---|---|---|
309
+ | **Retry** | Max retries | 3 |
310
+ | | Base delay | 500 ms |
311
+ | | Max delay | 10,000 ms |
312
+ | | Multiplier | 2x |
313
+ | **Priority Fee** | Target percentile | p75 |
314
+ | | Min fee | 1,000 microLamports |
315
+ | | Max fee | 1,000,000 microLamports |
316
+ | | Default compute units | 200,000 |
317
+ | **Confirmation** | Timeout | 60,000 ms |
318
+ | | Poll interval | 2,000 ms |
319
+ | | WebSocket | Enabled |
320
+ | | Commitment | `confirmed` |
321
+ | **Blockhash** | TTL | 60,000 ms |
322
+ | | Refresh interval | 30,000 ms |
323
+ | **Jito** | Block engine | mainnet |
324
+ | | Min tip | 1,000 lamports |
325
+
326
+ ## Key Types
327
+
328
+ ```typescript
329
+ import type {
330
+ SenderConfig,
331
+ SendResult,
332
+ SendOptions,
333
+ RetryConfig,
334
+ RetryContext,
335
+ FeeEstimateConfig,
336
+ FeeEstimateResult,
337
+ ComputeBudgetConfig,
338
+ ConnectionPoolConfig,
339
+ RpcEndpointConfig,
340
+ HealthMetrics,
341
+ CircuitBreakerConfig,
342
+ JitoConfig,
343
+ BundleResult,
344
+ BundleStatus,
345
+ SimulationConfig,
346
+ SimulationResult,
347
+ ConfirmationConfig,
348
+ ConfirmationResult,
349
+ BlockhashManagerConfig,
350
+ BlockhashInfo,
351
+ TxEventMap,
352
+ Logger,
353
+ } from "solana-tx-kit";
354
+ ```
355
+
356
+ ## Contributing
357
+
358
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
359
+
360
+ ```bash
361
+ pnpm install # install dependencies
362
+ pnpm test # run tests
363
+ pnpm lint # lint and format
364
+ pnpm build # build ESM + CJS output
365
+ ```
366
+
367
+ ## License
368
+
369
+ [MIT](LICENSE)