quicknode-solana-kit 1.0.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 +495 -0
- package/dist/addons.d.ts +3 -0
- package/dist/addons.d.ts.map +1 -0
- package/dist/addons.js +128 -0
- package/dist/addons.js.map +1 -0
- package/dist/assets/das.d.ts +8 -0
- package/dist/assets/das.d.ts.map +1 -0
- package/dist/assets/das.js +74 -0
- package/dist/assets/das.js.map +1 -0
- package/dist/assets/index.d.ts +2 -0
- package/dist/assets/index.d.ts.map +1 -0
- package/dist/assets/index.js +11 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/streaming/index.d.ts +2 -0
- package/dist/streaming/index.d.ts.map +1 -0
- package/dist/streaming/index.js +8 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/streaming/watcher.d.ts +5 -0
- package/dist/streaming/watcher.d.ts.map +1 -0
- package/dist/streaming/watcher.js +89 -0
- package/dist/streaming/watcher.js.map +1 -0
- package/dist/swap/index.d.ts +2 -0
- package/dist/swap/index.d.ts.map +1 -0
- package/dist/swap/index.js +8 -0
- package/dist/swap/index.js.map +1 -0
- package/dist/swap/jupiter.d.ts +16 -0
- package/dist/swap/jupiter.d.ts.map +1 -0
- package/dist/swap/jupiter.js +57 -0
- package/dist/swap/jupiter.js.map +1 -0
- package/dist/transactions/index.d.ts +3 -0
- package/dist/transactions/index.d.ts.map +1 -0
- package/dist/transactions/index.js +10 -0
- package/dist/transactions/index.js.map +1 -0
- package/dist/transactions/priority-fees.d.ts +4 -0
- package/dist/transactions/priority-fees.d.ts.map +1 -0
- package/dist/transactions/priority-fees.js +30 -0
- package/dist/transactions/priority-fees.js.map +1 -0
- package/dist/transactions/smart-transaction.d.ts +17 -0
- package/dist/transactions/smart-transaction.d.ts.map +1 -0
- package/dist/transactions/smart-transaction.js +112 -0
- package/dist/transactions/smart-transaction.js.map +1 -0
- package/dist/types/errors.d.ts +61 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +139 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +211 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/addon-guard.d.ts +25 -0
- package/dist/utils/addon-guard.d.ts.map +1 -0
- package/dist/utils/addon-guard.js +67 -0
- package/dist/utils/addon-guard.js.map +1 -0
- package/dist/utils/helpers.d.ts +10 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +49 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/http.d.ts +4 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +88 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +20 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +82 -0
package/README.md
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
# @quicknode/solana-kit
|
|
2
|
+
|
|
3
|
+
**The unified TypeScript SDK for every QuickNode Solana add-on.**
|
|
4
|
+
|
|
5
|
+
One install. One config. Priority fees, Jito bundles, NFT queries, Jupiter swaps, and live streaming — all typed, all in one place.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @quicknode/solana-kit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What Is This?
|
|
14
|
+
|
|
15
|
+
This is an **npm package for Solana developers**.
|
|
16
|
+
|
|
17
|
+
Every developer building on Solana using QuickNode has to wire up each add-on manually — read separate docs pages, figure out separate API shapes, write hundreds of lines of boilerplate glue code. This SDK replaces all of that.
|
|
18
|
+
|
|
19
|
+
**Without this SDK:**
|
|
20
|
+
```ts
|
|
21
|
+
// ~200 lines of manual code every time:
|
|
22
|
+
// - call qn_estimatePriorityFees, parse it
|
|
23
|
+
// - figure out which level to pick
|
|
24
|
+
// - build SetComputeUnitLimit instruction
|
|
25
|
+
// - build SetComputeUnitPrice instruction
|
|
26
|
+
// - remove existing compute budget ixs to avoid duplicates
|
|
27
|
+
// - simulate to estimate compute units
|
|
28
|
+
// - handle retry when blockhash expires
|
|
29
|
+
// - confirm and return useful data
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**With this SDK:**
|
|
33
|
+
```ts
|
|
34
|
+
const result = await kit.sendSmartTransaction({ transaction, signer });
|
|
35
|
+
// That's it. Everything above is handled.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- Node.js 18+
|
|
43
|
+
- A QuickNode Solana endpoint → **free at [dashboard.quicknode.com](https://dashboard.quicknode.com)**
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Quickstart
|
|
48
|
+
|
|
49
|
+
### 1. Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install @quicknode/solana-kit
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Get a QuickNode endpoint
|
|
56
|
+
|
|
57
|
+
Go to [dashboard.quicknode.com](https://dashboard.quicknode.com) → Create Endpoint → Solana.
|
|
58
|
+
Copy the endpoint URL. It looks like:
|
|
59
|
+
```
|
|
60
|
+
https://your-name.solana-mainnet.quiknode.pro/YOUR_TOKEN/
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. (Optional) Enable free add-ons
|
|
64
|
+
|
|
65
|
+
In the QuickNode dashboard → your endpoint → Add-ons tab:
|
|
66
|
+
- Enable **Priority Fee API** (free) → unlocks `estimatePriorityFees()` + auto-fees in `sendSmartTransaction()`
|
|
67
|
+
- Enable **Metaplex DAS API** (free) → unlocks all NFT query methods
|
|
68
|
+
|
|
69
|
+
### 4. Create the kit
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { QNSolanaKit } from '@quicknode/solana-kit';
|
|
73
|
+
|
|
74
|
+
const kit = new QNSolanaKit({
|
|
75
|
+
endpointUrl: 'https://your-name.solana-mainnet.quiknode.pro/TOKEN/',
|
|
76
|
+
|
|
77
|
+
// Tell the SDK which add-ons you've enabled.
|
|
78
|
+
// The SDK works without any — it just shows helpful messages
|
|
79
|
+
// when you call a method that needs an add-on you haven't enabled.
|
|
80
|
+
addOns: {
|
|
81
|
+
priorityFees: true, // FREE — Priority Fee API
|
|
82
|
+
das: true, // FREE — Metaplex DAS API
|
|
83
|
+
metis: false, // PAID — Metis (Jupiter Swap)
|
|
84
|
+
liljit: false, // PAID — Lil' JIT (Jito bundles)
|
|
85
|
+
yellowstone: false, // PAID — Yellowstone gRPC
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 5. Check what's available
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Probes your endpoint and shows exactly which add-ons are active
|
|
94
|
+
npm run check
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Output:
|
|
98
|
+
```
|
|
99
|
+
@quicknode/solana-kit — Add-on Status
|
|
100
|
+
──────────────────────────────────────────────────────
|
|
101
|
+
Endpoint: https://your-name.solana-mainnet.quiknode.pro...
|
|
102
|
+
|
|
103
|
+
✓ Priority Fee API [FREE]
|
|
104
|
+
enabled — Dynamic priority fee estimation for faster tx inclusion
|
|
105
|
+
|
|
106
|
+
✓ Metaplex DAS API [FREE]
|
|
107
|
+
enabled — Query NFTs, compressed NFTs, token metadata
|
|
108
|
+
|
|
109
|
+
✗ Metis — Jupiter V6 Swap API [PAID]
|
|
110
|
+
not enabled — Best-price token swaps via Jupiter aggregator
|
|
111
|
+
Enable: https://marketplace.quicknode.com/add-ons/metis-jupiter-v6-swap-api
|
|
112
|
+
|
|
113
|
+
Features Available:
|
|
114
|
+
✓ Smart Transactions (auto fees + retry)
|
|
115
|
+
✓ NFT / Digital Asset Queries
|
|
116
|
+
✗ Jupiter Token Swaps
|
|
117
|
+
✗ Yellowstone gRPC Streaming
|
|
118
|
+
✗ Jito Bundle (MEV protection)
|
|
119
|
+
✓ WebSocket Streaming (slot/account/logs)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Add-on Tiers
|
|
125
|
+
|
|
126
|
+
| Add-on | Tier | What it unlocks |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| Priority Fee API | **FREE** | `estimatePriorityFees()`, auto-fees in `sendSmartTransaction()` |
|
|
129
|
+
| Metaplex DAS API | **FREE** | `getAssetsByOwner()`, `getAsset()`, `searchAssets()`, `getAssetProof()` |
|
|
130
|
+
| Metis (Jupiter Swap) | **PAID** | `swap()`, `getSwapQuote()` |
|
|
131
|
+
| Lil' JIT (Jito) | **PAID** | `sendSmartTransaction({ options: { useJito: true } })` |
|
|
132
|
+
| Yellowstone gRPC | **PAID** | `watchAccount()` uses gRPC instead of WebSocket |
|
|
133
|
+
|
|
134
|
+
**Everything except swaps and Jito works on free tier.**
|
|
135
|
+
WebSocket streaming (`watchAccount`, `watchProgram`, `watchSlot`) works out of the box with no add-ons.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## API Reference
|
|
140
|
+
|
|
141
|
+
### `kit.sendSmartTransaction(params)`
|
|
142
|
+
|
|
143
|
+
Send a transaction with automatic priority fees, compute unit optimization, and retry.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
const result = await kit.sendSmartTransaction({
|
|
147
|
+
transaction: myTx, // your @solana/web3.js Transaction
|
|
148
|
+
signer: keypair, // Keypair that signs + pays fees
|
|
149
|
+
options: {
|
|
150
|
+
feeLevel: 'recommended', // 'low'|'medium'|'recommended'|'high'|'extreme'
|
|
151
|
+
useJito: false, // true = Jito bundle (requires liljit add-on)
|
|
152
|
+
maxRetries: 5, // retries on blockhash expiry
|
|
153
|
+
simulateFirst: true, // simulate to right-size compute units
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
console.log(result.signature); // 'abc123...'
|
|
158
|
+
console.log(result.slot); // 320481923
|
|
159
|
+
console.log(result.priorityFeeMicroLamports); // 5000
|
|
160
|
+
console.log(result.computeUnitsUsed); // 12500
|
|
161
|
+
console.log(result.confirmationMs); // 843
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### `kit.prepareSmartTransaction(params)`
|
|
167
|
+
|
|
168
|
+
Same as `sendSmartTransaction` but returns the prepared transaction without sending.
|
|
169
|
+
**Use this with wallet adapters** (Phantom, Backpack, etc.) where you don't hold the key.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
const { transaction } = await kit.prepareSmartTransaction({
|
|
173
|
+
transaction: myTx,
|
|
174
|
+
payer: wallet.publicKey,
|
|
175
|
+
options: { feeLevel: 'high' },
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Pass to wallet adapter:
|
|
179
|
+
const sig = await wallet.sendTransaction(transaction, kit.connection);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
### `kit.estimatePriorityFees(options?)`
|
|
185
|
+
|
|
186
|
+
Get current priority fees for all levels.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
const fees = await kit.estimatePriorityFees({
|
|
190
|
+
account: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4', // optional filter
|
|
191
|
+
lastNBlocks: 100,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
fees.low // 500 µlamports/CU
|
|
195
|
+
fees.medium // 1200 µlamports/CU
|
|
196
|
+
fees.recommended // 3000 µlamports/CU
|
|
197
|
+
fees.high // 8000 µlamports/CU
|
|
198
|
+
fees.extreme // 25000 µlamports/CU
|
|
199
|
+
fees.networkCongestion // 0.42 (0=quiet, 1=congested)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### `kit.getAssetsByOwner(options)`
|
|
205
|
+
|
|
206
|
+
Get all NFTs, cNFTs, and tokens owned by a wallet.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
const { items, total } = await kit.getAssetsByOwner({
|
|
210
|
+
ownerAddress: 'WalletAddress',
|
|
211
|
+
limit: 50,
|
|
212
|
+
sortBy: 'created',
|
|
213
|
+
sortDirection: 'desc',
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
items.forEach(asset => {
|
|
217
|
+
console.log(asset.content.metadata.name);
|
|
218
|
+
console.log(asset.ownership.owner);
|
|
219
|
+
console.log(asset.compression?.compressed); // true = cNFT
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### `kit.getAsset(mintAddress)`
|
|
226
|
+
|
|
227
|
+
Get a single digital asset by mint address.
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
const asset = await kit.getAsset('NFTMintAddress');
|
|
231
|
+
console.log(asset.content.metadata.name);
|
|
232
|
+
console.log(asset.content.metadata.image);
|
|
233
|
+
console.log(asset.royalty?.basis_points);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### `kit.searchAssets(options)`
|
|
239
|
+
|
|
240
|
+
Search with filters: owner, creator, collection, token type.
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const { items } = await kit.searchAssets({
|
|
244
|
+
ownerAddress: 'WalletAddress',
|
|
245
|
+
tokenType: 'compressedNFT', // only cNFTs
|
|
246
|
+
limit: 100,
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
### `kit.getAssetProof(assetId)`
|
|
253
|
+
|
|
254
|
+
Get the Merkle proof for a compressed NFT. Required for cNFT transfers via Bubblegum.
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const proof = await kit.getAssetProof('cNFTAssetId');
|
|
258
|
+
// proof.root + proof.proof → use with mpl-bubblegum transfer instruction
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
### `kit.getTokenAccounts(walletAddress)`
|
|
264
|
+
|
|
265
|
+
Get all SPL token accounts and balances for a wallet.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const tokens = await kit.getTokenAccounts('WalletAddress');
|
|
269
|
+
tokens
|
|
270
|
+
.filter(t => t.uiAmount > 0)
|
|
271
|
+
.forEach(t => console.log(`${t.mint}: ${t.uiAmount}`));
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
### `kit.watchAccount(address, callback, options?)`
|
|
277
|
+
|
|
278
|
+
Watch an account for real-time updates. Auto-uses Yellowstone if enabled, WebSocket otherwise.
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
const handle = kit.watchAccount('WalletAddress', (update) => {
|
|
282
|
+
console.log('SOL balance:', update.lamports / 1e9);
|
|
283
|
+
console.log('Backend:', update.backend); // 'yellowstone' or 'websocket'
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Stop watching:
|
|
287
|
+
handle.unsubscribe();
|
|
288
|
+
handle.isConnected(); // boolean
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
### `kit.watchProgram(programId, callback, options?)`
|
|
294
|
+
|
|
295
|
+
Watch all transactions for a program in real time.
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
const JUPITER = 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4';
|
|
299
|
+
|
|
300
|
+
const handle = kit.watchProgram(JUPITER, (tx) => {
|
|
301
|
+
if (!tx.err) console.log('Swap:', tx.signature);
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### `kit.watchSlot(callback)`
|
|
308
|
+
|
|
309
|
+
Subscribe to slot updates (~every 400ms). Free tier, no add-ons needed.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
const handle = kit.watchSlot((slot) => {
|
|
313
|
+
process.stdout.write(`\r Slot: ${slot.toLocaleString()}`);
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
### `kit.getSwapQuote(options)` — PAID (Metis)
|
|
320
|
+
|
|
321
|
+
Get a Jupiter swap quote without executing.
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
import { TOKENS } from '@quicknode/solana-kit';
|
|
325
|
+
|
|
326
|
+
const quote = await kit.getSwapQuote({
|
|
327
|
+
inputMint: TOKENS.SOL,
|
|
328
|
+
outputMint: TOKENS.USDC,
|
|
329
|
+
amount: BigInt(1_000_000_000), // 1 SOL
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
console.log(`1 SOL → ${Number(quote.outAmount) / 1e6} USDC`);
|
|
333
|
+
console.log(`Price impact: ${quote.priceImpactPct}%`);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### `kit.swap(options)` — PAID (Metis)
|
|
339
|
+
|
|
340
|
+
Execute a token swap in one call.
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
const result = await kit.swap({
|
|
344
|
+
inputMint: TOKENS.SOL,
|
|
345
|
+
outputMint: TOKENS.USDC,
|
|
346
|
+
amount: BigInt(100_000_000), // 0.1 SOL
|
|
347
|
+
userPublicKey: keypair.publicKey.toString(),
|
|
348
|
+
signer: keypair,
|
|
349
|
+
slippageBps: 50, // 0.5%
|
|
350
|
+
feeLevel: 'recommended',
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
console.log('Swapped!', result.signature);
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Token shortcuts: `TOKENS.SOL`, `TOKENS.USDC`, `TOKENS.USDT`, `TOKENS.BONK`, `TOKENS.JUP`, `TOKENS.RAY`, `TOKENS.WIF`
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### `kit.checkAddOns()`
|
|
361
|
+
|
|
362
|
+
Probe your endpoint to see which add-ons are live.
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
const status = await kit.checkAddOns();
|
|
366
|
+
console.log(status.canUse.smartTransactions); // true/false
|
|
367
|
+
console.log(status.canUse.nftQueries); // true/false
|
|
368
|
+
console.log(status.addOns); // per-add-on detail
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Error Handling
|
|
374
|
+
|
|
375
|
+
Every error is typed and tells you exactly what went wrong and how to fix it.
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
import {
|
|
379
|
+
AddOnNotEnabledError,
|
|
380
|
+
TransactionFailedError,
|
|
381
|
+
TransactionTimeoutError,
|
|
382
|
+
MaxRetriesExceededError,
|
|
383
|
+
RPCError,
|
|
384
|
+
} from '@quicknode/solana-kit';
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
await kit.sendSmartTransaction({ transaction, signer });
|
|
388
|
+
} catch (err) {
|
|
389
|
+
if (err instanceof AddOnNotEnabledError) {
|
|
390
|
+
// Clear message with link to enable the add-on in the dashboard
|
|
391
|
+
console.error(err.message);
|
|
392
|
+
} else if (err instanceof TransactionFailedError) {
|
|
393
|
+
// On-chain failure — check logs
|
|
394
|
+
console.error('Failed on-chain:', err.reason);
|
|
395
|
+
console.error('Explorer:', `https://explorer.solana.com/tx/${err.signature}`);
|
|
396
|
+
} else if (err instanceof TransactionTimeoutError) {
|
|
397
|
+
console.error('Timed out. Try a higher feeLevel.');
|
|
398
|
+
} else if (err instanceof MaxRetriesExceededError) {
|
|
399
|
+
console.error(`Failed after ${err.maxRetries} retries.`);
|
|
400
|
+
} else if (err instanceof RPCError) {
|
|
401
|
+
console.error(`RPC ${err.statusCode}:`, err.message);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## Tree-Shaking
|
|
409
|
+
|
|
410
|
+
Import only what you need for smaller bundles:
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
// Individual function imports (fully tree-shakeable)
|
|
414
|
+
import { estimatePriorityFees } from '@quicknode/solana-kit';
|
|
415
|
+
import { getAssetsByOwner } from '@quicknode/solana-kit';
|
|
416
|
+
import { watchAccount } from '@quicknode/solana-kit';
|
|
417
|
+
import { swap, TOKENS } from '@quicknode/solana-kit';
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Running the Examples
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
# Clone and install
|
|
426
|
+
git clone https://github.com/quiknode-labs/qn-solana-kit
|
|
427
|
+
cd qn-solana-kit
|
|
428
|
+
npm install
|
|
429
|
+
|
|
430
|
+
# Setup environment
|
|
431
|
+
cp .env.example .env
|
|
432
|
+
# Fill in QN_ENDPOINT_URL (required)
|
|
433
|
+
# Fill in WALLET_ADDRESS and WALLET_PRIVATE_KEY for tx/swap examples
|
|
434
|
+
|
|
435
|
+
# Check which add-ons are active on your endpoint
|
|
436
|
+
npm run check
|
|
437
|
+
|
|
438
|
+
# Run individual examples
|
|
439
|
+
npm run example:tx # Smart transaction (needs Priority Fee API add-on)
|
|
440
|
+
npm run example:nft # NFT queries (needs DAS API add-on)
|
|
441
|
+
npm run example:stream # Live streaming (works on free tier)
|
|
442
|
+
npm run example:swap # Jupiter swap quote (needs Metis add-on)
|
|
443
|
+
|
|
444
|
+
# Run all
|
|
445
|
+
npm run example:all
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Project Structure
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
src/
|
|
454
|
+
index.ts ← QNSolanaKit class + all exports
|
|
455
|
+
addons.ts ← checkAddOns() diagnostic
|
|
456
|
+
types/
|
|
457
|
+
index.ts ← All TypeScript types
|
|
458
|
+
errors.ts ← Typed error classes
|
|
459
|
+
utils/
|
|
460
|
+
helpers.ts ← Connection, sleep, etc.
|
|
461
|
+
http.ts ← rpcPost, httpGet, httpPost
|
|
462
|
+
addon-guard.ts ← requireAddOn, isAddOnEnabled
|
|
463
|
+
transactions/
|
|
464
|
+
priority-fees.ts ← estimatePriorityFees
|
|
465
|
+
smart-transaction.ts ← sendSmartTransaction, prepareSmartTransaction
|
|
466
|
+
assets/
|
|
467
|
+
das.ts ← getAssetsByOwner, getAsset, searchAssets, etc.
|
|
468
|
+
streaming/
|
|
469
|
+
watcher.ts ← watchAccount, watchProgram, watchSlot
|
|
470
|
+
swap/
|
|
471
|
+
jupiter.ts ← swap, getSwapQuote, TOKENS
|
|
472
|
+
examples/
|
|
473
|
+
1-smart-transaction.ts
|
|
474
|
+
2-nft-queries.ts
|
|
475
|
+
3-live-streaming.ts
|
|
476
|
+
4-jupiter-swap.ts
|
|
477
|
+
run-all.ts
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Links
|
|
483
|
+
|
|
484
|
+
- QuickNode Dashboard: https://dashboard.quicknode.com
|
|
485
|
+
- Add-ons Marketplace: https://marketplace.quicknode.com
|
|
486
|
+
- Priority Fee API: https://www.quicknode.com/docs/solana/qn_estimatePriorityFees
|
|
487
|
+
- Metaplex DAS API: https://www.quicknode.com/docs/solana/getAsset
|
|
488
|
+
- Metis (Jupiter): https://marketplace.quicknode.com/add-ons/metis-jupiter-v6-swap-api
|
|
489
|
+
- Yellowstone gRPC: https://marketplace.quicknode.com/add-ons/yellowstone-grpc
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## License
|
|
494
|
+
|
|
495
|
+
MIT © QuickNode
|
package/dist/addons.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addons.d.ts","sourceRoot":"","sources":["../src/addons.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAe,MAAM,SAAS,CAAC;AAwGvE,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC,GAChD,OAAO,CAAC,gBAAgB,CAAC,CAmB3B"}
|
package/dist/addons.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkAddOns = checkAddOns;
|
|
4
|
+
const http_1 = require("./utils/http");
|
|
5
|
+
function isEnabledError(err) {
|
|
6
|
+
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
7
|
+
return !(msg.includes('method not found') ||
|
|
8
|
+
msg.includes('-32601') ||
|
|
9
|
+
msg.includes('does not exist') ||
|
|
10
|
+
msg.includes('not supported') ||
|
|
11
|
+
msg.includes('403') ||
|
|
12
|
+
msg.includes('unauthorized'));
|
|
13
|
+
}
|
|
14
|
+
const DEFS = [
|
|
15
|
+
{
|
|
16
|
+
key: 'priorityFees',
|
|
17
|
+
name: 'Priority Fee API',
|
|
18
|
+
tier: 'free',
|
|
19
|
+
description: 'Dynamic priority fee estimation for faster tx inclusion',
|
|
20
|
+
enableUrl: 'https://marketplace.quicknode.com/add-ons/solana-priority-fee',
|
|
21
|
+
probe: async (url) => {
|
|
22
|
+
try {
|
|
23
|
+
const res = await fetch(url, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json' },
|
|
26
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'qn_estimatePriorityFees', params: { last_n_blocks: 10, api_version: 2 } }),
|
|
27
|
+
signal: AbortSignal.timeout(8_000),
|
|
28
|
+
});
|
|
29
|
+
const data = await res.json();
|
|
30
|
+
if (data.result !== undefined)
|
|
31
|
+
return true;
|
|
32
|
+
if (data.error)
|
|
33
|
+
return isEnabledError(new Error(`${data.error.code}: ${data.error.message}`));
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: 'das',
|
|
43
|
+
name: 'Metaplex DAS API',
|
|
44
|
+
tier: 'free',
|
|
45
|
+
description: 'Query NFTs, compressed NFTs, token metadata',
|
|
46
|
+
enableUrl: 'https://marketplace.quicknode.com/add-ons/metaplex-das-api',
|
|
47
|
+
probe: async (url) => {
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(url, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: { 'Content-Type': 'application/json' },
|
|
52
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getAssetsByOwner', params: { ownerAddress: 'E645TckHQnDcavVv92Etc6xSWQaq8zzPtPRGBheviRAk', limit: 1, page: 1 } }),
|
|
53
|
+
signal: AbortSignal.timeout(8_000),
|
|
54
|
+
});
|
|
55
|
+
const data = await res.json();
|
|
56
|
+
if (data.result !== undefined)
|
|
57
|
+
return true;
|
|
58
|
+
if (data.error)
|
|
59
|
+
return isEnabledError(new Error(`${data.error.code}: ${data.error.message}`));
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: 'metis',
|
|
69
|
+
name: 'Metis — Jupiter V6 Swap API',
|
|
70
|
+
tier: 'paid',
|
|
71
|
+
description: 'Best-price token swaps via Jupiter aggregator',
|
|
72
|
+
enableUrl: 'https://marketplace.quicknode.com/add-ons/metis-jupiter-v6-swap-api',
|
|
73
|
+
probe: async (url) => {
|
|
74
|
+
try {
|
|
75
|
+
const base = url.replace(/\/$/, '');
|
|
76
|
+
const res = await fetch(`${base}/jupiter/v6/quote?inputMint=So11111111111111111111111111111111111111112&outputMint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=1000000&slippageBps=50`, { signal: AbortSignal.timeout(5_000) });
|
|
77
|
+
return res.status === 200;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
key: 'liljit',
|
|
86
|
+
name: "Lil' JIT (Jito Bundles)",
|
|
87
|
+
tier: 'paid',
|
|
88
|
+
description: 'MEV protection + atomic transaction execution via Jito',
|
|
89
|
+
enableUrl: 'https://marketplace.quicknode.com/add-ons/lil-jit',
|
|
90
|
+
probe: async (url) => {
|
|
91
|
+
try {
|
|
92
|
+
await (0, http_1.rpcPost)(url, 'sendBundle', [[]], 5_000);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
return isEnabledError(err);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
key: 'yellowstone',
|
|
102
|
+
name: 'Yellowstone gRPC',
|
|
103
|
+
tier: 'paid',
|
|
104
|
+
description: 'Ultra-low-latency gRPC account + program streaming',
|
|
105
|
+
enableUrl: 'https://marketplace.quicknode.com/add-ons/yellowstone-grpc',
|
|
106
|
+
probe: async () => false,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
async function checkAddOns(config) {
|
|
110
|
+
const statuses = [];
|
|
111
|
+
for (const def of DEFS) {
|
|
112
|
+
const enabled = await def.probe(config.endpointUrl);
|
|
113
|
+
statuses.push({ name: def.name, enabled, tier: def.tier, description: def.description, enableUrl: def.enableUrl });
|
|
114
|
+
}
|
|
115
|
+
const get = (key) => statuses.find(s => DEFS.find(d => d.key === key)?.name === s.name)?.enabled ?? false;
|
|
116
|
+
return {
|
|
117
|
+
endpoint: config.endpointUrl,
|
|
118
|
+
addOns: statuses,
|
|
119
|
+
canUse: {
|
|
120
|
+
smartTransactions: get('priorityFees'),
|
|
121
|
+
nftQueries: get('das'),
|
|
122
|
+
swaps: get('metis'),
|
|
123
|
+
yellowstoneStream: get('yellowstone'),
|
|
124
|
+
jitoBundle: get('liljit'),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=addons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addons.js","sourceRoot":"","sources":["../src/addons.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { QNConfig, DigitalAsset, AssetsResult, AssetProof, TokenAccount, GetAssetsByOwnerOptions, GetAssetsByCollectionOptions, SearchAssetsOptions } from '../types';
|
|
2
|
+
export declare function getAssetsByOwner(config: QNConfig, options: GetAssetsByOwnerOptions): Promise<AssetsResult>;
|
|
3
|
+
export declare function getAsset(config: QNConfig, mintAddress: string): Promise<DigitalAsset>;
|
|
4
|
+
export declare function getAssetsByCollection(config: QNConfig, options: GetAssetsByCollectionOptions): Promise<AssetsResult>;
|
|
5
|
+
export declare function searchAssets(config: QNConfig, options: SearchAssetsOptions): Promise<AssetsResult>;
|
|
6
|
+
export declare function getAssetProof(config: QNConfig, assetId: string): Promise<AssetProof>;
|
|
7
|
+
export declare function getTokenAccounts(config: QNConfig, walletAddress: string): Promise<TokenAccount[]>;
|
|
8
|
+
//# sourceMappingURL=das.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"das.d.ts","sourceRoot":"","sources":["../../src/assets/das.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAC9D,uBAAuB,EAAE,4BAA4B,EAAE,mBAAmB,EAC3E,MAAM,UAAU,CAAC;AAOlB,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC,CAOhH;AAGD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAG3F;AAGD,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,YAAY,CAAC,CAQ1H;AAGD,wBAAsB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAYxG;AAGD,wBAAsB,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAG1F;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBvG"}
|