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 +21 -0
- package/README.md +369 -0
- package/dist/index.cjs +1297 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +675 -0
- package/dist/index.d.ts +675 -0
- package/dist/index.js +1266 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
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
|
+
[](https://www.npmjs.com/package/solana-tx-kit)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](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)
|