@solana/web3.js 0.0.0-next

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.
Files changed (53) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +158 -0
  3. package/lib/index.browser.cjs.js +10062 -0
  4. package/lib/index.browser.cjs.js.map +1 -0
  5. package/lib/index.browser.esm.js +9976 -0
  6. package/lib/index.browser.esm.js.map +1 -0
  7. package/lib/index.cjs.js +9568 -0
  8. package/lib/index.cjs.js.map +1 -0
  9. package/lib/index.d.ts +3311 -0
  10. package/lib/index.esm.js +9479 -0
  11. package/lib/index.esm.js.map +1 -0
  12. package/lib/index.iife.js +30136 -0
  13. package/lib/index.iife.js.map +1 -0
  14. package/lib/index.iife.min.js +40 -0
  15. package/lib/index.iife.min.js.map +1 -0
  16. package/package.json +140 -0
  17. package/src/account.ts +46 -0
  18. package/src/agent-manager.ts +44 -0
  19. package/src/blockhash.ts +4 -0
  20. package/src/bpf-loader-deprecated.ts +5 -0
  21. package/src/bpf-loader.ts +45 -0
  22. package/src/connection.ts +4935 -0
  23. package/src/ed25519-program.ts +157 -0
  24. package/src/epoch-schedule.ts +102 -0
  25. package/src/errors.ts +9 -0
  26. package/src/fee-calculator.ts +16 -0
  27. package/src/index.ts +31 -0
  28. package/src/instruction.ts +58 -0
  29. package/src/keypair.ts +98 -0
  30. package/src/layout.ts +147 -0
  31. package/src/loader.ts +236 -0
  32. package/src/message.ts +271 -0
  33. package/src/nonce-account.ts +78 -0
  34. package/src/publickey.ts +296 -0
  35. package/src/secp256k1-program.ts +229 -0
  36. package/src/stake-program.ts +923 -0
  37. package/src/system-program.ts +1007 -0
  38. package/src/sysvar.ts +37 -0
  39. package/src/timing.ts +23 -0
  40. package/src/transaction.ts +808 -0
  41. package/src/util/assert.ts +8 -0
  42. package/src/util/borsh-schema.ts +38 -0
  43. package/src/util/cluster.ts +31 -0
  44. package/src/util/promise-timeout.ts +14 -0
  45. package/src/util/send-and-confirm-raw-transaction.ts +46 -0
  46. package/src/util/send-and-confirm-transaction.ts +50 -0
  47. package/src/util/shortvec-encoding.ts +28 -0
  48. package/src/util/sleep.ts +4 -0
  49. package/src/util/to-buffer.ts +11 -0
  50. package/src/util/url.ts +18 -0
  51. package/src/validator-info.ts +106 -0
  52. package/src/vote-account.ts +236 -0
  53. package/src/vote-program.ts +413 -0
@@ -0,0 +1,4935 @@
1
+ import bs58 from 'bs58';
2
+ import {Buffer} from 'buffer';
3
+ import crossFetch from 'cross-fetch';
4
+ // @ts-ignore
5
+ import fastStableStringify from 'fast-stable-stringify';
6
+ import {
7
+ type as pick,
8
+ number,
9
+ string,
10
+ array,
11
+ boolean,
12
+ literal,
13
+ record,
14
+ union,
15
+ optional,
16
+ nullable,
17
+ coerce,
18
+ instance,
19
+ create,
20
+ tuple,
21
+ unknown,
22
+ any,
23
+ } from 'superstruct';
24
+ import type {Struct} from 'superstruct';
25
+ import {Client as RpcWebSocketClient} from 'rpc-websockets';
26
+ import RpcClient from 'jayson/lib/client/browser';
27
+ import {IWSRequestParams} from 'rpc-websockets/dist/lib/client';
28
+
29
+ import {AgentManager} from './agent-manager';
30
+ import {EpochSchedule} from './epoch-schedule';
31
+ import {SendTransactionError} from './errors';
32
+ import {NonceAccount} from './nonce-account';
33
+ import {PublicKey} from './publickey';
34
+ import {Signer} from './keypair';
35
+ import {MS_PER_SLOT} from './timing';
36
+ import {Transaction} from './transaction';
37
+ import {Message} from './message';
38
+ import assert from './util/assert';
39
+ import {sleep} from './util/sleep';
40
+ import {promiseTimeout} from './util/promise-timeout';
41
+ import {toBuffer} from './util/to-buffer';
42
+ import {makeWebsocketUrl} from './util/url';
43
+ import type {Blockhash} from './blockhash';
44
+ import type {FeeCalculator} from './fee-calculator';
45
+ import type {TransactionSignature} from './transaction';
46
+ import type {CompiledInstruction} from './message';
47
+
48
+ const PublicKeyFromString = coerce(
49
+ instance(PublicKey),
50
+ string(),
51
+ value => new PublicKey(value),
52
+ );
53
+
54
+ const RawAccountDataResult = tuple([string(), literal('base64')]);
55
+
56
+ const BufferFromRawAccountData = coerce(
57
+ instance(Buffer),
58
+ RawAccountDataResult,
59
+ value => Buffer.from(value[0], 'base64'),
60
+ );
61
+
62
+ /**
63
+ * Attempt to use a recent blockhash for up to 30 seconds
64
+ * @internal
65
+ */
66
+ export const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000;
67
+
68
+ type ClientSubscriptionId = number;
69
+ /** @internal */ type ServerSubscriptionId = number;
70
+ /** @internal */ type SubscriptionConfigHash = string;
71
+ /** @internal */ type SubscriptionDisposeFn = () => Promise<void>;
72
+ /**
73
+ * @internal
74
+ * Every subscription has a list of callers interested in publishes.
75
+ */
76
+ type BaseSubscription<TMethod = SubscriptionConfig['method']> = Readonly<{
77
+ callbacks: Set<Extract<SubscriptionConfig, {method: TMethod}>['callback']>;
78
+ }>;
79
+ /**
80
+ * @internal
81
+ * A subscription may be in various states of connectedness. Only when it is
82
+ * fully connected will it have a server subscription id associated with it.
83
+ * This id can be returned to the server to unsubscribe the client entirely.
84
+ */
85
+ type StatefulSubscription = Readonly<
86
+ // New subscriptions that have not yet been
87
+ // sent to the server start in this state.
88
+ | {
89
+ state: 'pending';
90
+ }
91
+ // These subscriptions have been sent to the server
92
+ // and are waiting for the server to acknowledge them.
93
+ | {
94
+ state: 'subscribing';
95
+ }
96
+ // These subscriptions have been acknowledged by the
97
+ // server and have been assigned server subscription ids.
98
+ | {
99
+ serverSubscriptionId: ServerSubscriptionId;
100
+ state: 'subscribed';
101
+ }
102
+ // These subscriptions are intended to be torn down and
103
+ // are waiting on an acknowledgement from the server.
104
+ | {
105
+ serverSubscriptionId: ServerSubscriptionId;
106
+ state: 'unsubscribing';
107
+ }
108
+ // The request to tear down these subscriptions has been
109
+ // acknowledged by the server. The `serverSubscriptionId`
110
+ // is the id of the now-dead subscription.
111
+ | {
112
+ serverSubscriptionId: ServerSubscriptionId;
113
+ state: 'unsubscribed';
114
+ }
115
+ >;
116
+ /**
117
+ * Different RPC subscription methods accept different params.
118
+ */
119
+ type SubscriptionConfig = Readonly<
120
+ | {
121
+ callback: AccountChangeCallback;
122
+ method: 'accountSubscribe';
123
+ params: Readonly<{
124
+ commitment?: Commitment; // Must default to 'finalized'
125
+ publicKey: string; // PublicKey of the account as a base 58 string
126
+ }>;
127
+ unsubscribeMethod: 'accountUnsubscribe';
128
+ }
129
+ | {
130
+ callback: LogsCallback;
131
+ method: 'logsSubscribe';
132
+ params: Readonly<{
133
+ commitment?: Commitment; // Must default to 'finalized'
134
+ filter: LogsFilter;
135
+ }>;
136
+ unsubscribeMethod: 'logsUnsubscribe';
137
+ }
138
+ | {
139
+ callback: ProgramAccountChangeCallback;
140
+ method: 'programSubscribe';
141
+ params: Readonly<{
142
+ commitment?: Commitment; // Must default to 'finalized'
143
+ filters?: GetProgramAccountsFilter[];
144
+ programId: string; // PublicKey of the program as a base 58 string
145
+ }>;
146
+ unsubscribeMethod: 'programUnsubscribe';
147
+ }
148
+ | {
149
+ callback: RootChangeCallback;
150
+ method: 'rootSubscribe';
151
+ params: undefined;
152
+ unsubscribeMethod: 'rootUnsubscribe';
153
+ }
154
+ | {
155
+ callback: SignatureSubscriptionCallback;
156
+ method: 'signatureSubscribe';
157
+ params: Readonly<{
158
+ signature: TransactionSignature; // TransactionSignature as a base 58 string
159
+ options?: SignatureSubscriptionOptions;
160
+ }>;
161
+ unsubscribeMethod: 'signatureUnsubscribe';
162
+ }
163
+ | {
164
+ callback: SlotChangeCallback;
165
+ method: 'slotSubscribe';
166
+ params: undefined;
167
+ unsubscribeMethod: 'slotUnsubscribe';
168
+ }
169
+ | {
170
+ callback: SlotUpdateCallback;
171
+ method: 'slotsUpdatesSubscribe';
172
+ params: undefined;
173
+ unsubscribeMethod: 'slotsUpdatesUnsubscribe';
174
+ }
175
+ >;
176
+ /**
177
+ * @internal
178
+ * Utility type that keeps tagged unions intact while omitting properties.
179
+ */
180
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
181
+ ? Omit<T, K>
182
+ : never;
183
+ type Subscription = BaseSubscription &
184
+ StatefulSubscription &
185
+ DistributiveOmit<SubscriptionConfig, 'callback'>;
186
+
187
+ /**
188
+ * @internal
189
+ */
190
+ function hashSubscriptionConfig({
191
+ method,
192
+ params,
193
+ }: SubscriptionConfig): SubscriptionConfigHash {
194
+ return fastStableStringify([method, params], true /* isArrayProp */);
195
+ }
196
+
197
+ type RpcRequest = (methodName: string, args: Array<any>) => any;
198
+
199
+ type RpcBatchRequest = (requests: RpcParams[]) => any;
200
+
201
+ /**
202
+ * @internal
203
+ */
204
+ export type RpcParams = {
205
+ methodName: string;
206
+ args: Array<any>;
207
+ };
208
+
209
+ export type TokenAccountsFilter =
210
+ | {
211
+ mint: PublicKey;
212
+ }
213
+ | {
214
+ programId: PublicKey;
215
+ };
216
+
217
+ /**
218
+ * Extra contextual information for RPC responses
219
+ */
220
+ export type Context = {
221
+ slot: number;
222
+ };
223
+
224
+ /**
225
+ * Options for sending transactions
226
+ */
227
+ export type SendOptions = {
228
+ /** disable transaction verification step */
229
+ skipPreflight?: boolean;
230
+ /** preflight commitment level */
231
+ preflightCommitment?: Commitment;
232
+ /** Maximum number of times for the RPC node to retry sending the transaction to the leader. */
233
+ maxRetries?: number;
234
+ };
235
+
236
+ /**
237
+ * Options for confirming transactions
238
+ */
239
+ export type ConfirmOptions = {
240
+ /** disable transaction verification step */
241
+ skipPreflight?: boolean;
242
+ /** desired commitment level */
243
+ commitment?: Commitment;
244
+ /** preflight commitment level */
245
+ preflightCommitment?: Commitment;
246
+ /** Maximum number of times for the RPC node to retry sending the transaction to the leader. */
247
+ maxRetries?: number;
248
+ };
249
+
250
+ /**
251
+ * Options for getConfirmedSignaturesForAddress2
252
+ */
253
+ export type ConfirmedSignaturesForAddress2Options = {
254
+ /**
255
+ * Start searching backwards from this transaction signature.
256
+ * @remark If not provided the search starts from the highest max confirmed block.
257
+ */
258
+ before?: TransactionSignature;
259
+ /** Search until this transaction signature is reached, if found before `limit`. */
260
+ until?: TransactionSignature;
261
+ /** Maximum transaction signatures to return (between 1 and 1,000, default: 1,000). */
262
+ limit?: number;
263
+ };
264
+
265
+ /**
266
+ * Options for getSignaturesForAddress
267
+ */
268
+ export type SignaturesForAddressOptions = {
269
+ /**
270
+ * Start searching backwards from this transaction signature.
271
+ * @remark If not provided the search starts from the highest max confirmed block.
272
+ */
273
+ before?: TransactionSignature;
274
+ /** Search until this transaction signature is reached, if found before `limit`. */
275
+ until?: TransactionSignature;
276
+ /** Maximum transaction signatures to return (between 1 and 1,000, default: 1,000). */
277
+ limit?: number;
278
+ };
279
+
280
+ /**
281
+ * RPC Response with extra contextual information
282
+ */
283
+ export type RpcResponseAndContext<T> = {
284
+ /** response context */
285
+ context: Context;
286
+ /** response value */
287
+ value: T;
288
+ };
289
+
290
+ /**
291
+ * @internal
292
+ */
293
+ function createRpcResult<T, U>(result: Struct<T, U>) {
294
+ return union([
295
+ pick({
296
+ jsonrpc: literal('2.0'),
297
+ id: string(),
298
+ result,
299
+ }),
300
+ pick({
301
+ jsonrpc: literal('2.0'),
302
+ id: string(),
303
+ error: pick({
304
+ code: unknown(),
305
+ message: string(),
306
+ data: optional(any()),
307
+ }),
308
+ }),
309
+ ]);
310
+ }
311
+
312
+ const UnknownRpcResult = createRpcResult(unknown());
313
+
314
+ /**
315
+ * @internal
316
+ */
317
+ function jsonRpcResult<T, U>(schema: Struct<T, U>) {
318
+ return coerce(createRpcResult(schema), UnknownRpcResult, value => {
319
+ if ('error' in value) {
320
+ return value;
321
+ } else {
322
+ return {
323
+ ...value,
324
+ result: create(value.result, schema),
325
+ };
326
+ }
327
+ });
328
+ }
329
+
330
+ /**
331
+ * @internal
332
+ */
333
+ function jsonRpcResultAndContext<T, U>(value: Struct<T, U>) {
334
+ return jsonRpcResult(
335
+ pick({
336
+ context: pick({
337
+ slot: number(),
338
+ }),
339
+ value,
340
+ }),
341
+ );
342
+ }
343
+
344
+ /**
345
+ * @internal
346
+ */
347
+ function notificationResultAndContext<T, U>(value: Struct<T, U>) {
348
+ return pick({
349
+ context: pick({
350
+ slot: number(),
351
+ }),
352
+ value,
353
+ });
354
+ }
355
+
356
+ /**
357
+ * The level of commitment desired when querying state
358
+ * <pre>
359
+ * 'processed': Query the most recent block which has reached 1 confirmation by the connected node
360
+ * 'confirmed': Query the most recent block which has reached 1 confirmation by the cluster
361
+ * 'finalized': Query the most recent block which has been finalized by the cluster
362
+ * </pre>
363
+ */
364
+ export type Commitment =
365
+ | 'processed'
366
+ | 'confirmed'
367
+ | 'finalized'
368
+ | 'recent' // Deprecated as of v1.5.5
369
+ | 'single' // Deprecated as of v1.5.5
370
+ | 'singleGossip' // Deprecated as of v1.5.5
371
+ | 'root' // Deprecated as of v1.5.5
372
+ | 'max'; // Deprecated as of v1.5.5
373
+
374
+ /**
375
+ * A subset of Commitment levels, which are at least optimistically confirmed
376
+ * <pre>
377
+ * 'confirmed': Query the most recent block which has reached 1 confirmation by the cluster
378
+ * 'finalized': Query the most recent block which has been finalized by the cluster
379
+ * </pre>
380
+ */
381
+ export type Finality = 'confirmed' | 'finalized';
382
+
383
+ /**
384
+ * Filter for largest accounts query
385
+ * <pre>
386
+ * 'circulating': Return the largest accounts that are part of the circulating supply
387
+ * 'nonCirculating': Return the largest accounts that are not part of the circulating supply
388
+ * </pre>
389
+ */
390
+ export type LargestAccountsFilter = 'circulating' | 'nonCirculating';
391
+
392
+ /**
393
+ * Configuration object for changing `getLargestAccounts` query behavior
394
+ */
395
+ export type GetLargestAccountsConfig = {
396
+ /** The level of commitment desired */
397
+ commitment?: Commitment;
398
+ /** Filter largest accounts by whether they are part of the circulating supply */
399
+ filter?: LargestAccountsFilter;
400
+ };
401
+
402
+ /**
403
+ * Configuration object for changing `getSupply` request behavior
404
+ */
405
+ export type GetSupplyConfig = {
406
+ /** The level of commitment desired */
407
+ commitment?: Commitment;
408
+ /** Exclude non circulating accounts list from response */
409
+ excludeNonCirculatingAccountsList?: boolean;
410
+ };
411
+
412
+ /**
413
+ * Configuration object for changing query behavior
414
+ */
415
+ export type SignatureStatusConfig = {
416
+ /** enable searching status history, not needed for recent transactions */
417
+ searchTransactionHistory: boolean;
418
+ };
419
+
420
+ /**
421
+ * Information describing a cluster node
422
+ */
423
+ export type ContactInfo = {
424
+ /** Identity public key of the node */
425
+ pubkey: string;
426
+ /** Gossip network address for the node */
427
+ gossip: string | null;
428
+ /** TPU network address for the node (null if not available) */
429
+ tpu: string | null;
430
+ /** JSON RPC network address for the node (null if not available) */
431
+ rpc: string | null;
432
+ /** Software version of the node (null if not available) */
433
+ version: string | null;
434
+ };
435
+
436
+ /**
437
+ * Information describing a vote account
438
+ */
439
+ export type VoteAccountInfo = {
440
+ /** Public key of the vote account */
441
+ votePubkey: string;
442
+ /** Identity public key of the node voting with this account */
443
+ nodePubkey: string;
444
+ /** The stake, in lamports, delegated to this vote account and activated */
445
+ activatedStake: number;
446
+ /** Whether the vote account is staked for this epoch */
447
+ epochVoteAccount: boolean;
448
+ /** Recent epoch voting credit history for this voter */
449
+ epochCredits: Array<[number, number, number]>;
450
+ /** A percentage (0-100) of rewards payout owed to the voter */
451
+ commission: number;
452
+ /** Most recent slot voted on by this vote account */
453
+ lastVote: number;
454
+ };
455
+
456
+ /**
457
+ * A collection of cluster vote accounts
458
+ */
459
+ export type VoteAccountStatus = {
460
+ /** Active vote accounts */
461
+ current: Array<VoteAccountInfo>;
462
+ /** Inactive vote accounts */
463
+ delinquent: Array<VoteAccountInfo>;
464
+ };
465
+
466
+ /**
467
+ * Network Inflation
468
+ * (see https://docs.solana.com/implemented-proposals/ed_overview)
469
+ */
470
+ export type InflationGovernor = {
471
+ foundation: number;
472
+ foundationTerm: number;
473
+ initial: number;
474
+ taper: number;
475
+ terminal: number;
476
+ };
477
+
478
+ const GetInflationGovernorResult = pick({
479
+ foundation: number(),
480
+ foundationTerm: number(),
481
+ initial: number(),
482
+ taper: number(),
483
+ terminal: number(),
484
+ });
485
+
486
+ /**
487
+ * The inflation reward for an epoch
488
+ */
489
+ export type InflationReward = {
490
+ /** epoch for which the reward occurs */
491
+ epoch: number;
492
+ /** the slot in which the rewards are effective */
493
+ effectiveSlot: number;
494
+ /** reward amount in lamports */
495
+ amount: number;
496
+ /** post balance of the account in lamports */
497
+ postBalance: number;
498
+ };
499
+
500
+ /**
501
+ * Expected JSON RPC response for the "getInflationReward" message
502
+ */
503
+ const GetInflationRewardResult = jsonRpcResult(
504
+ array(
505
+ nullable(
506
+ pick({
507
+ epoch: number(),
508
+ effectiveSlot: number(),
509
+ amount: number(),
510
+ postBalance: number(),
511
+ }),
512
+ ),
513
+ ),
514
+ );
515
+
516
+ /**
517
+ * Information about the current epoch
518
+ */
519
+ export type EpochInfo = {
520
+ epoch: number;
521
+ slotIndex: number;
522
+ slotsInEpoch: number;
523
+ absoluteSlot: number;
524
+ blockHeight?: number;
525
+ transactionCount?: number;
526
+ };
527
+
528
+ const GetEpochInfoResult = pick({
529
+ epoch: number(),
530
+ slotIndex: number(),
531
+ slotsInEpoch: number(),
532
+ absoluteSlot: number(),
533
+ blockHeight: optional(number()),
534
+ transactionCount: optional(number()),
535
+ });
536
+
537
+ const GetEpochScheduleResult = pick({
538
+ slotsPerEpoch: number(),
539
+ leaderScheduleSlotOffset: number(),
540
+ warmup: boolean(),
541
+ firstNormalEpoch: number(),
542
+ firstNormalSlot: number(),
543
+ });
544
+
545
+ /**
546
+ * Leader schedule
547
+ * (see https://docs.solana.com/terminology#leader-schedule)
548
+ */
549
+ export type LeaderSchedule = {
550
+ [address: string]: number[];
551
+ };
552
+
553
+ const GetLeaderScheduleResult = record(string(), array(number()));
554
+
555
+ /**
556
+ * Transaction error or null
557
+ */
558
+ const TransactionErrorResult = nullable(union([pick({}), string()]));
559
+
560
+ /**
561
+ * Signature status for a transaction
562
+ */
563
+ const SignatureStatusResult = pick({
564
+ err: TransactionErrorResult,
565
+ });
566
+
567
+ /**
568
+ * Transaction signature received notification
569
+ */
570
+ const SignatureReceivedResult = literal('receivedSignature');
571
+
572
+ /**
573
+ * Version info for a node
574
+ */
575
+ export type Version = {
576
+ /** Version of solana-core */
577
+ 'solana-core': string;
578
+ 'feature-set'?: number;
579
+ };
580
+
581
+ const VersionResult = pick({
582
+ 'solana-core': string(),
583
+ 'feature-set': optional(number()),
584
+ });
585
+
586
+ export type SimulatedTransactionAccountInfo = {
587
+ /** `true` if this account's data contains a loaded program */
588
+ executable: boolean;
589
+ /** Identifier of the program that owns the account */
590
+ owner: string;
591
+ /** Number of lamports assigned to the account */
592
+ lamports: number;
593
+ /** Optional data assigned to the account */
594
+ data: string[];
595
+ /** Optional rent epoch info for account */
596
+ rentEpoch?: number;
597
+ };
598
+
599
+ export type SimulatedTransactionResponse = {
600
+ err: TransactionError | string | null;
601
+ logs: Array<string> | null;
602
+ accounts?: (SimulatedTransactionAccountInfo | null)[] | null;
603
+ unitsConsumed?: number;
604
+ };
605
+
606
+ const SimulatedTransactionResponseStruct = jsonRpcResultAndContext(
607
+ pick({
608
+ err: nullable(union([pick({}), string()])),
609
+ logs: nullable(array(string())),
610
+ accounts: optional(
611
+ nullable(
612
+ array(
613
+ nullable(
614
+ pick({
615
+ executable: boolean(),
616
+ owner: string(),
617
+ lamports: number(),
618
+ data: array(string()),
619
+ rentEpoch: optional(number()),
620
+ }),
621
+ ),
622
+ ),
623
+ ),
624
+ ),
625
+ unitsConsumed: optional(number()),
626
+ }),
627
+ );
628
+
629
+ export type ParsedInnerInstruction = {
630
+ index: number;
631
+ instructions: (ParsedInstruction | PartiallyDecodedInstruction)[];
632
+ };
633
+
634
+ export type TokenBalance = {
635
+ accountIndex: number;
636
+ mint: string;
637
+ owner?: string;
638
+ uiTokenAmount: TokenAmount;
639
+ };
640
+
641
+ /**
642
+ * Metadata for a parsed confirmed transaction on the ledger
643
+ *
644
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link ParsedTransactionMeta} instead.
645
+ */
646
+ export type ParsedConfirmedTransactionMeta = ParsedTransactionMeta;
647
+
648
+ /**
649
+ * Metadata for a parsed transaction on the ledger
650
+ */
651
+ export type ParsedTransactionMeta = {
652
+ /** The fee charged for processing the transaction */
653
+ fee: number;
654
+ /** An array of cross program invoked parsed instructions */
655
+ innerInstructions?: ParsedInnerInstruction[] | null;
656
+ /** The balances of the transaction accounts before processing */
657
+ preBalances: Array<number>;
658
+ /** The balances of the transaction accounts after processing */
659
+ postBalances: Array<number>;
660
+ /** An array of program log messages emitted during a transaction */
661
+ logMessages?: Array<string> | null;
662
+ /** The token balances of the transaction accounts before processing */
663
+ preTokenBalances?: Array<TokenBalance> | null;
664
+ /** The token balances of the transaction accounts after processing */
665
+ postTokenBalances?: Array<TokenBalance> | null;
666
+ /** The error result of transaction processing */
667
+ err: TransactionError | null;
668
+ };
669
+
670
+ export type CompiledInnerInstruction = {
671
+ index: number;
672
+ instructions: CompiledInstruction[];
673
+ };
674
+
675
+ /**
676
+ * Metadata for a confirmed transaction on the ledger
677
+ */
678
+ export type ConfirmedTransactionMeta = {
679
+ /** The fee charged for processing the transaction */
680
+ fee: number;
681
+ /** An array of cross program invoked instructions */
682
+ innerInstructions?: CompiledInnerInstruction[] | null;
683
+ /** The balances of the transaction accounts before processing */
684
+ preBalances: Array<number>;
685
+ /** The balances of the transaction accounts after processing */
686
+ postBalances: Array<number>;
687
+ /** An array of program log messages emitted during a transaction */
688
+ logMessages?: Array<string> | null;
689
+ /** The token balances of the transaction accounts before processing */
690
+ preTokenBalances?: Array<TokenBalance> | null;
691
+ /** The token balances of the transaction accounts after processing */
692
+ postTokenBalances?: Array<TokenBalance> | null;
693
+ /** The error result of transaction processing */
694
+ err: TransactionError | null;
695
+ };
696
+
697
+ /**
698
+ * A processed transaction from the RPC API
699
+ */
700
+ export type TransactionResponse = {
701
+ /** The slot during which the transaction was processed */
702
+ slot: number;
703
+ /** The transaction */
704
+ transaction: {
705
+ /** The transaction message */
706
+ message: Message;
707
+ /** The transaction signatures */
708
+ signatures: string[];
709
+ };
710
+ /** Metadata produced from the transaction */
711
+ meta: ConfirmedTransactionMeta | null;
712
+ /** The unix timestamp of when the transaction was processed */
713
+ blockTime?: number | null;
714
+ };
715
+
716
+ /**
717
+ * A confirmed transaction on the ledger
718
+ */
719
+ export type ConfirmedTransaction = {
720
+ /** The slot during which the transaction was processed */
721
+ slot: number;
722
+ /** The details of the transaction */
723
+ transaction: Transaction;
724
+ /** Metadata produced from the transaction */
725
+ meta: ConfirmedTransactionMeta | null;
726
+ /** The unix timestamp of when the transaction was processed */
727
+ blockTime?: number | null;
728
+ };
729
+
730
+ /**
731
+ * A partially decoded transaction instruction
732
+ */
733
+ export type PartiallyDecodedInstruction = {
734
+ /** Program id called by this instruction */
735
+ programId: PublicKey;
736
+ /** Public keys of accounts passed to this instruction */
737
+ accounts: Array<PublicKey>;
738
+ /** Raw base-58 instruction data */
739
+ data: string;
740
+ };
741
+
742
+ /**
743
+ * A parsed transaction message account
744
+ */
745
+ export type ParsedMessageAccount = {
746
+ /** Public key of the account */
747
+ pubkey: PublicKey;
748
+ /** Indicates if the account signed the transaction */
749
+ signer: boolean;
750
+ /** Indicates if the account is writable for this transaction */
751
+ writable: boolean;
752
+ };
753
+
754
+ /**
755
+ * A parsed transaction instruction
756
+ */
757
+ export type ParsedInstruction = {
758
+ /** Name of the program for this instruction */
759
+ program: string;
760
+ /** ID of the program for this instruction */
761
+ programId: PublicKey;
762
+ /** Parsed instruction info */
763
+ parsed: any;
764
+ };
765
+
766
+ /**
767
+ * A parsed transaction message
768
+ */
769
+ export type ParsedMessage = {
770
+ /** Accounts used in the instructions */
771
+ accountKeys: ParsedMessageAccount[];
772
+ /** The atomically executed instructions for the transaction */
773
+ instructions: (ParsedInstruction | PartiallyDecodedInstruction)[];
774
+ /** Recent blockhash */
775
+ recentBlockhash: string;
776
+ };
777
+
778
+ /**
779
+ * A parsed transaction
780
+ */
781
+ export type ParsedTransaction = {
782
+ /** Signatures for the transaction */
783
+ signatures: Array<string>;
784
+ /** Message of the transaction */
785
+ message: ParsedMessage;
786
+ };
787
+
788
+ /**
789
+ * A parsed and confirmed transaction on the ledger
790
+ *
791
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link ParsedTransactionWithMeta} instead.
792
+ */
793
+ export type ParsedConfirmedTransaction = ParsedTransactionWithMeta;
794
+
795
+ /**
796
+ * A parsed transaction on the ledger with meta
797
+ */
798
+ export type ParsedTransactionWithMeta = {
799
+ /** The slot during which the transaction was processed */
800
+ slot: number;
801
+ /** The details of the transaction */
802
+ transaction: ParsedTransaction;
803
+ /** Metadata produced from the transaction */
804
+ meta: ParsedTransactionMeta | null;
805
+ /** The unix timestamp of when the transaction was processed */
806
+ blockTime?: number | null;
807
+ };
808
+
809
+ /**
810
+ * A processed block fetched from the RPC API
811
+ */
812
+ export type BlockResponse = {
813
+ /** Blockhash of this block */
814
+ blockhash: Blockhash;
815
+ /** Blockhash of this block's parent */
816
+ previousBlockhash: Blockhash;
817
+ /** Slot index of this block's parent */
818
+ parentSlot: number;
819
+ /** Vector of transactions with status meta and original message */
820
+ transactions: Array<{
821
+ /** The transaction */
822
+ transaction: {
823
+ /** The transaction message */
824
+ message: Message;
825
+ /** The transaction signatures */
826
+ signatures: string[];
827
+ };
828
+ /** Metadata produced from the transaction */
829
+ meta: ConfirmedTransactionMeta | null;
830
+ }>;
831
+ /** Vector of block rewards */
832
+ rewards?: Array<{
833
+ /** Public key of reward recipient */
834
+ pubkey: string;
835
+ /** Reward value in lamports */
836
+ lamports: number;
837
+ /** Account balance after reward is applied */
838
+ postBalance: number | null;
839
+ /** Type of reward received */
840
+ rewardType: string | null;
841
+ }>;
842
+ /** The unix timestamp of when the block was processed */
843
+ blockTime: number | null;
844
+ };
845
+
846
+ /**
847
+ * A ConfirmedBlock on the ledger
848
+ */
849
+ export type ConfirmedBlock = {
850
+ /** Blockhash of this block */
851
+ blockhash: Blockhash;
852
+ /** Blockhash of this block's parent */
853
+ previousBlockhash: Blockhash;
854
+ /** Slot index of this block's parent */
855
+ parentSlot: number;
856
+ /** Vector of transactions and status metas */
857
+ transactions: Array<{
858
+ transaction: Transaction;
859
+ meta: ConfirmedTransactionMeta | null;
860
+ }>;
861
+ /** Vector of block rewards */
862
+ rewards?: Array<{
863
+ pubkey: string;
864
+ lamports: number;
865
+ postBalance: number | null;
866
+ rewardType: string | null;
867
+ }>;
868
+ /** The unix timestamp of when the block was processed */
869
+ blockTime: number | null;
870
+ };
871
+
872
+ /**
873
+ * A Block on the ledger with signatures only
874
+ */
875
+ export type BlockSignatures = {
876
+ /** Blockhash of this block */
877
+ blockhash: Blockhash;
878
+ /** Blockhash of this block's parent */
879
+ previousBlockhash: Blockhash;
880
+ /** Slot index of this block's parent */
881
+ parentSlot: number;
882
+ /** Vector of signatures */
883
+ signatures: Array<string>;
884
+ /** The unix timestamp of when the block was processed */
885
+ blockTime: number | null;
886
+ };
887
+
888
+ /**
889
+ * recent block production information
890
+ */
891
+ export type BlockProduction = Readonly<{
892
+ /** a dictionary of validator identities, as base-58 encoded strings. Value is a two element array containing the number of leader slots and the number of blocks produced */
893
+ byIdentity: Readonly<Record<string, ReadonlyArray<number>>>;
894
+ /** Block production slot range */
895
+ range: Readonly<{
896
+ /** first slot of the block production information (inclusive) */
897
+ firstSlot: number;
898
+ /** last slot of block production information (inclusive) */
899
+ lastSlot: number;
900
+ }>;
901
+ }>;
902
+
903
+ export type GetBlockProductionConfig = {
904
+ /** Optional commitment level */
905
+ commitment?: Commitment;
906
+ /** Slot range to return block production for. If parameter not provided, defaults to current epoch. */
907
+ range?: {
908
+ /** first slot to return block production information for (inclusive) */
909
+ firstSlot: number;
910
+ /** last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot */
911
+ lastSlot?: number;
912
+ };
913
+ /** Only return results for this validator identity (base-58 encoded) */
914
+ identity?: string;
915
+ };
916
+
917
+ /**
918
+ * Expected JSON RPC response for the "getBlockProduction" message
919
+ */
920
+ const BlockProductionResponseStruct = jsonRpcResultAndContext(
921
+ pick({
922
+ byIdentity: record(string(), array(number())),
923
+ range: pick({
924
+ firstSlot: number(),
925
+ lastSlot: number(),
926
+ }),
927
+ }),
928
+ );
929
+
930
+ /**
931
+ * A performance sample
932
+ */
933
+ export type PerfSample = {
934
+ /** Slot number of sample */
935
+ slot: number;
936
+ /** Number of transactions in a sample window */
937
+ numTransactions: number;
938
+ /** Number of slots in a sample window */
939
+ numSlots: number;
940
+ /** Sample window in seconds */
941
+ samplePeriodSecs: number;
942
+ };
943
+
944
+ function createRpcClient(
945
+ url: string,
946
+ useHttps: boolean,
947
+ httpHeaders?: HttpHeaders,
948
+ customFetch?: typeof crossFetch,
949
+ fetchMiddleware?: FetchMiddleware,
950
+ disableRetryOnRateLimit?: boolean,
951
+ ): RpcClient {
952
+ const fetch = customFetch ? customFetch : crossFetch;
953
+ let agentManager: AgentManager | undefined;
954
+ if (!process.env.BROWSER) {
955
+ agentManager = new AgentManager(useHttps);
956
+ }
957
+
958
+ let fetchWithMiddleware:
959
+ | ((url: string, options: any) => Promise<Response>)
960
+ | undefined;
961
+
962
+ if (fetchMiddleware) {
963
+ fetchWithMiddleware = async (url: string, options: any) => {
964
+ const modifiedFetchArgs = await new Promise<[string, any]>(
965
+ (resolve, reject) => {
966
+ try {
967
+ fetchMiddleware(url, options, (modifiedUrl, modifiedOptions) =>
968
+ resolve([modifiedUrl, modifiedOptions]),
969
+ );
970
+ } catch (error) {
971
+ reject(error);
972
+ }
973
+ },
974
+ );
975
+ return await fetch(...modifiedFetchArgs);
976
+ };
977
+ }
978
+
979
+ const clientBrowser = new RpcClient(async (request, callback) => {
980
+ const agent = agentManager ? agentManager.requestStart() : undefined;
981
+ const options = {
982
+ method: 'POST',
983
+ body: request,
984
+ agent,
985
+ headers: Object.assign(
986
+ {
987
+ 'Content-Type': 'application/json',
988
+ },
989
+ httpHeaders || {},
990
+ ),
991
+ };
992
+
993
+ try {
994
+ let too_many_requests_retries = 5;
995
+ let res: Response;
996
+ let waitTime = 500;
997
+ for (;;) {
998
+ if (fetchWithMiddleware) {
999
+ res = await fetchWithMiddleware(url, options);
1000
+ } else {
1001
+ res = await fetch(url, options);
1002
+ }
1003
+
1004
+ if (res.status !== 429 /* Too many requests */) {
1005
+ break;
1006
+ }
1007
+ if (disableRetryOnRateLimit === true) {
1008
+ break;
1009
+ }
1010
+ too_many_requests_retries -= 1;
1011
+ if (too_many_requests_retries === 0) {
1012
+ break;
1013
+ }
1014
+ console.log(
1015
+ `Server responded with ${res.status} ${res.statusText}. Retrying after ${waitTime}ms delay...`,
1016
+ );
1017
+ await sleep(waitTime);
1018
+ waitTime *= 2;
1019
+ }
1020
+
1021
+ const text = await res.text();
1022
+ if (res.ok) {
1023
+ callback(null, text);
1024
+ } else {
1025
+ callback(new Error(`${res.status} ${res.statusText}: ${text}`));
1026
+ }
1027
+ } catch (err) {
1028
+ if (err instanceof Error) callback(err);
1029
+ } finally {
1030
+ agentManager && agentManager.requestEnd();
1031
+ }
1032
+ }, {});
1033
+
1034
+ return clientBrowser;
1035
+ }
1036
+
1037
+ function createRpcRequest(client: RpcClient): RpcRequest {
1038
+ return (method, args) => {
1039
+ return new Promise((resolve, reject) => {
1040
+ client.request(method, args, (err: any, response: any) => {
1041
+ if (err) {
1042
+ reject(err);
1043
+ return;
1044
+ }
1045
+ resolve(response);
1046
+ });
1047
+ });
1048
+ };
1049
+ }
1050
+
1051
+ function createRpcBatchRequest(client: RpcClient): RpcBatchRequest {
1052
+ return (requests: RpcParams[]) => {
1053
+ return new Promise((resolve, reject) => {
1054
+ // Do nothing if requests is empty
1055
+ if (requests.length === 0) resolve([]);
1056
+
1057
+ const batch = requests.map((params: RpcParams) => {
1058
+ return client.request(params.methodName, params.args);
1059
+ });
1060
+
1061
+ client.request(batch, (err: any, response: any) => {
1062
+ if (err) {
1063
+ reject(err);
1064
+ return;
1065
+ }
1066
+ resolve(response);
1067
+ });
1068
+ });
1069
+ };
1070
+ }
1071
+
1072
+ /**
1073
+ * Expected JSON RPC response for the "getInflationGovernor" message
1074
+ */
1075
+ const GetInflationGovernorRpcResult = jsonRpcResult(GetInflationGovernorResult);
1076
+
1077
+ /**
1078
+ * Expected JSON RPC response for the "getEpochInfo" message
1079
+ */
1080
+ const GetEpochInfoRpcResult = jsonRpcResult(GetEpochInfoResult);
1081
+
1082
+ /**
1083
+ * Expected JSON RPC response for the "getEpochSchedule" message
1084
+ */
1085
+ const GetEpochScheduleRpcResult = jsonRpcResult(GetEpochScheduleResult);
1086
+
1087
+ /**
1088
+ * Expected JSON RPC response for the "getLeaderSchedule" message
1089
+ */
1090
+ const GetLeaderScheduleRpcResult = jsonRpcResult(GetLeaderScheduleResult);
1091
+
1092
+ /**
1093
+ * Expected JSON RPC response for the "minimumLedgerSlot" and "getFirstAvailableBlock" messages
1094
+ */
1095
+ const SlotRpcResult = jsonRpcResult(number());
1096
+
1097
+ /**
1098
+ * Supply
1099
+ */
1100
+ export type Supply = {
1101
+ /** Total supply in lamports */
1102
+ total: number;
1103
+ /** Circulating supply in lamports */
1104
+ circulating: number;
1105
+ /** Non-circulating supply in lamports */
1106
+ nonCirculating: number;
1107
+ /** List of non-circulating account addresses */
1108
+ nonCirculatingAccounts: Array<PublicKey>;
1109
+ };
1110
+
1111
+ /**
1112
+ * Expected JSON RPC response for the "getSupply" message
1113
+ */
1114
+ const GetSupplyRpcResult = jsonRpcResultAndContext(
1115
+ pick({
1116
+ total: number(),
1117
+ circulating: number(),
1118
+ nonCirculating: number(),
1119
+ nonCirculatingAccounts: array(PublicKeyFromString),
1120
+ }),
1121
+ );
1122
+
1123
+ /**
1124
+ * Token amount object which returns a token amount in different formats
1125
+ * for various client use cases.
1126
+ */
1127
+ export type TokenAmount = {
1128
+ /** Raw amount of tokens as string ignoring decimals */
1129
+ amount: string;
1130
+ /** Number of decimals configured for token's mint */
1131
+ decimals: number;
1132
+ /** Token amount as float, accounts for decimals */
1133
+ uiAmount: number | null;
1134
+ /** Token amount as string, accounts for decimals */
1135
+ uiAmountString?: string;
1136
+ };
1137
+
1138
+ /**
1139
+ * Expected JSON RPC structure for token amounts
1140
+ */
1141
+ const TokenAmountResult = pick({
1142
+ amount: string(),
1143
+ uiAmount: nullable(number()),
1144
+ decimals: number(),
1145
+ uiAmountString: optional(string()),
1146
+ });
1147
+
1148
+ /**
1149
+ * Token address and balance.
1150
+ */
1151
+ export type TokenAccountBalancePair = {
1152
+ /** Address of the token account */
1153
+ address: PublicKey;
1154
+ /** Raw amount of tokens as string ignoring decimals */
1155
+ amount: string;
1156
+ /** Number of decimals configured for token's mint */
1157
+ decimals: number;
1158
+ /** Token amount as float, accounts for decimals */
1159
+ uiAmount: number | null;
1160
+ /** Token amount as string, accounts for decimals */
1161
+ uiAmountString?: string;
1162
+ };
1163
+
1164
+ /**
1165
+ * Expected JSON RPC response for the "getTokenLargestAccounts" message
1166
+ */
1167
+ const GetTokenLargestAccountsResult = jsonRpcResultAndContext(
1168
+ array(
1169
+ pick({
1170
+ address: PublicKeyFromString,
1171
+ amount: string(),
1172
+ uiAmount: nullable(number()),
1173
+ decimals: number(),
1174
+ uiAmountString: optional(string()),
1175
+ }),
1176
+ ),
1177
+ );
1178
+
1179
+ /**
1180
+ * Expected JSON RPC response for the "getTokenAccountsByOwner" message
1181
+ */
1182
+ const GetTokenAccountsByOwner = jsonRpcResultAndContext(
1183
+ array(
1184
+ pick({
1185
+ pubkey: PublicKeyFromString,
1186
+ account: pick({
1187
+ executable: boolean(),
1188
+ owner: PublicKeyFromString,
1189
+ lamports: number(),
1190
+ data: BufferFromRawAccountData,
1191
+ rentEpoch: number(),
1192
+ }),
1193
+ }),
1194
+ ),
1195
+ );
1196
+
1197
+ const ParsedAccountDataResult = pick({
1198
+ program: string(),
1199
+ parsed: unknown(),
1200
+ space: number(),
1201
+ });
1202
+
1203
+ /**
1204
+ * Expected JSON RPC response for the "getTokenAccountsByOwner" message with parsed data
1205
+ */
1206
+ const GetParsedTokenAccountsByOwner = jsonRpcResultAndContext(
1207
+ array(
1208
+ pick({
1209
+ pubkey: PublicKeyFromString,
1210
+ account: pick({
1211
+ executable: boolean(),
1212
+ owner: PublicKeyFromString,
1213
+ lamports: number(),
1214
+ data: ParsedAccountDataResult,
1215
+ rentEpoch: number(),
1216
+ }),
1217
+ }),
1218
+ ),
1219
+ );
1220
+
1221
+ /**
1222
+ * Pair of an account address and its balance
1223
+ */
1224
+ export type AccountBalancePair = {
1225
+ address: PublicKey;
1226
+ lamports: number;
1227
+ };
1228
+
1229
+ /**
1230
+ * Expected JSON RPC response for the "getLargestAccounts" message
1231
+ */
1232
+ const GetLargestAccountsRpcResult = jsonRpcResultAndContext(
1233
+ array(
1234
+ pick({
1235
+ lamports: number(),
1236
+ address: PublicKeyFromString,
1237
+ }),
1238
+ ),
1239
+ );
1240
+
1241
+ /**
1242
+ * @internal
1243
+ */
1244
+ const AccountInfoResult = pick({
1245
+ executable: boolean(),
1246
+ owner: PublicKeyFromString,
1247
+ lamports: number(),
1248
+ data: BufferFromRawAccountData,
1249
+ rentEpoch: number(),
1250
+ });
1251
+
1252
+ /**
1253
+ * @internal
1254
+ */
1255
+ const KeyedAccountInfoResult = pick({
1256
+ pubkey: PublicKeyFromString,
1257
+ account: AccountInfoResult,
1258
+ });
1259
+
1260
+ const ParsedOrRawAccountData = coerce(
1261
+ union([instance(Buffer), ParsedAccountDataResult]),
1262
+ union([RawAccountDataResult, ParsedAccountDataResult]),
1263
+ value => {
1264
+ if (Array.isArray(value)) {
1265
+ return create(value, BufferFromRawAccountData);
1266
+ } else {
1267
+ return value;
1268
+ }
1269
+ },
1270
+ );
1271
+
1272
+ /**
1273
+ * @internal
1274
+ */
1275
+ const ParsedAccountInfoResult = pick({
1276
+ executable: boolean(),
1277
+ owner: PublicKeyFromString,
1278
+ lamports: number(),
1279
+ data: ParsedOrRawAccountData,
1280
+ rentEpoch: number(),
1281
+ });
1282
+
1283
+ const KeyedParsedAccountInfoResult = pick({
1284
+ pubkey: PublicKeyFromString,
1285
+ account: ParsedAccountInfoResult,
1286
+ });
1287
+
1288
+ /**
1289
+ * @internal
1290
+ */
1291
+ const StakeActivationResult = pick({
1292
+ state: union([
1293
+ literal('active'),
1294
+ literal('inactive'),
1295
+ literal('activating'),
1296
+ literal('deactivating'),
1297
+ ]),
1298
+ active: number(),
1299
+ inactive: number(),
1300
+ });
1301
+
1302
+ /**
1303
+ * Expected JSON RPC response for the "getConfirmedSignaturesForAddress2" message
1304
+ */
1305
+
1306
+ const GetConfirmedSignaturesForAddress2RpcResult = jsonRpcResult(
1307
+ array(
1308
+ pick({
1309
+ signature: string(),
1310
+ slot: number(),
1311
+ err: TransactionErrorResult,
1312
+ memo: nullable(string()),
1313
+ blockTime: optional(nullable(number())),
1314
+ }),
1315
+ ),
1316
+ );
1317
+
1318
+ /**
1319
+ * Expected JSON RPC response for the "getSignaturesForAddress" message
1320
+ */
1321
+ const GetSignaturesForAddressRpcResult = jsonRpcResult(
1322
+ array(
1323
+ pick({
1324
+ signature: string(),
1325
+ slot: number(),
1326
+ err: TransactionErrorResult,
1327
+ memo: nullable(string()),
1328
+ blockTime: optional(nullable(number())),
1329
+ }),
1330
+ ),
1331
+ );
1332
+
1333
+ /***
1334
+ * Expected JSON RPC response for the "accountNotification" message
1335
+ */
1336
+ const AccountNotificationResult = pick({
1337
+ subscription: number(),
1338
+ result: notificationResultAndContext(AccountInfoResult),
1339
+ });
1340
+
1341
+ /**
1342
+ * @internal
1343
+ */
1344
+ const ProgramAccountInfoResult = pick({
1345
+ pubkey: PublicKeyFromString,
1346
+ account: AccountInfoResult,
1347
+ });
1348
+
1349
+ /***
1350
+ * Expected JSON RPC response for the "programNotification" message
1351
+ */
1352
+ const ProgramAccountNotificationResult = pick({
1353
+ subscription: number(),
1354
+ result: notificationResultAndContext(ProgramAccountInfoResult),
1355
+ });
1356
+
1357
+ /**
1358
+ * @internal
1359
+ */
1360
+ const SlotInfoResult = pick({
1361
+ parent: number(),
1362
+ slot: number(),
1363
+ root: number(),
1364
+ });
1365
+
1366
+ /**
1367
+ * Expected JSON RPC response for the "slotNotification" message
1368
+ */
1369
+ const SlotNotificationResult = pick({
1370
+ subscription: number(),
1371
+ result: SlotInfoResult,
1372
+ });
1373
+
1374
+ /**
1375
+ * Slot updates which can be used for tracking the live progress of a cluster.
1376
+ * - `"firstShredReceived"`: connected node received the first shred of a block.
1377
+ * Indicates that a new block that is being produced.
1378
+ * - `"completed"`: connected node has received all shreds of a block. Indicates
1379
+ * a block was recently produced.
1380
+ * - `"optimisticConfirmation"`: block was optimistically confirmed by the
1381
+ * cluster. It is not guaranteed that an optimistic confirmation notification
1382
+ * will be sent for every finalized blocks.
1383
+ * - `"root"`: the connected node rooted this block.
1384
+ * - `"createdBank"`: the connected node has started validating this block.
1385
+ * - `"frozen"`: the connected node has validated this block.
1386
+ * - `"dead"`: the connected node failed to validate this block.
1387
+ */
1388
+ export type SlotUpdate =
1389
+ | {
1390
+ type: 'firstShredReceived';
1391
+ slot: number;
1392
+ timestamp: number;
1393
+ }
1394
+ | {
1395
+ type: 'completed';
1396
+ slot: number;
1397
+ timestamp: number;
1398
+ }
1399
+ | {
1400
+ type: 'createdBank';
1401
+ slot: number;
1402
+ timestamp: number;
1403
+ parent: number;
1404
+ }
1405
+ | {
1406
+ type: 'frozen';
1407
+ slot: number;
1408
+ timestamp: number;
1409
+ stats: {
1410
+ numTransactionEntries: number;
1411
+ numSuccessfulTransactions: number;
1412
+ numFailedTransactions: number;
1413
+ maxTransactionsPerEntry: number;
1414
+ };
1415
+ }
1416
+ | {
1417
+ type: 'dead';
1418
+ slot: number;
1419
+ timestamp: number;
1420
+ err: string;
1421
+ }
1422
+ | {
1423
+ type: 'optimisticConfirmation';
1424
+ slot: number;
1425
+ timestamp: number;
1426
+ }
1427
+ | {
1428
+ type: 'root';
1429
+ slot: number;
1430
+ timestamp: number;
1431
+ };
1432
+
1433
+ /**
1434
+ * @internal
1435
+ */
1436
+ const SlotUpdateResult = union([
1437
+ pick({
1438
+ type: union([
1439
+ literal('firstShredReceived'),
1440
+ literal('completed'),
1441
+ literal('optimisticConfirmation'),
1442
+ literal('root'),
1443
+ ]),
1444
+ slot: number(),
1445
+ timestamp: number(),
1446
+ }),
1447
+ pick({
1448
+ type: literal('createdBank'),
1449
+ parent: number(),
1450
+ slot: number(),
1451
+ timestamp: number(),
1452
+ }),
1453
+ pick({
1454
+ type: literal('frozen'),
1455
+ slot: number(),
1456
+ timestamp: number(),
1457
+ stats: pick({
1458
+ numTransactionEntries: number(),
1459
+ numSuccessfulTransactions: number(),
1460
+ numFailedTransactions: number(),
1461
+ maxTransactionsPerEntry: number(),
1462
+ }),
1463
+ }),
1464
+ pick({
1465
+ type: literal('dead'),
1466
+ slot: number(),
1467
+ timestamp: number(),
1468
+ err: string(),
1469
+ }),
1470
+ ]);
1471
+
1472
+ /**
1473
+ * Expected JSON RPC response for the "slotsUpdatesNotification" message
1474
+ */
1475
+ const SlotUpdateNotificationResult = pick({
1476
+ subscription: number(),
1477
+ result: SlotUpdateResult,
1478
+ });
1479
+
1480
+ /**
1481
+ * Expected JSON RPC response for the "signatureNotification" message
1482
+ */
1483
+ const SignatureNotificationResult = pick({
1484
+ subscription: number(),
1485
+ result: notificationResultAndContext(
1486
+ union([SignatureStatusResult, SignatureReceivedResult]),
1487
+ ),
1488
+ });
1489
+
1490
+ /**
1491
+ * Expected JSON RPC response for the "rootNotification" message
1492
+ */
1493
+ const RootNotificationResult = pick({
1494
+ subscription: number(),
1495
+ result: number(),
1496
+ });
1497
+
1498
+ const ContactInfoResult = pick({
1499
+ pubkey: string(),
1500
+ gossip: nullable(string()),
1501
+ tpu: nullable(string()),
1502
+ rpc: nullable(string()),
1503
+ version: nullable(string()),
1504
+ });
1505
+
1506
+ const VoteAccountInfoResult = pick({
1507
+ votePubkey: string(),
1508
+ nodePubkey: string(),
1509
+ activatedStake: number(),
1510
+ epochVoteAccount: boolean(),
1511
+ epochCredits: array(tuple([number(), number(), number()])),
1512
+ commission: number(),
1513
+ lastVote: number(),
1514
+ rootSlot: nullable(number()),
1515
+ });
1516
+
1517
+ /**
1518
+ * Expected JSON RPC response for the "getVoteAccounts" message
1519
+ */
1520
+ const GetVoteAccounts = jsonRpcResult(
1521
+ pick({
1522
+ current: array(VoteAccountInfoResult),
1523
+ delinquent: array(VoteAccountInfoResult),
1524
+ }),
1525
+ );
1526
+
1527
+ const ConfirmationStatus = union([
1528
+ literal('processed'),
1529
+ literal('confirmed'),
1530
+ literal('finalized'),
1531
+ ]);
1532
+
1533
+ const SignatureStatusResponse = pick({
1534
+ slot: number(),
1535
+ confirmations: nullable(number()),
1536
+ err: TransactionErrorResult,
1537
+ confirmationStatus: optional(ConfirmationStatus),
1538
+ });
1539
+
1540
+ /**
1541
+ * Expected JSON RPC response for the "getSignatureStatuses" message
1542
+ */
1543
+ const GetSignatureStatusesRpcResult = jsonRpcResultAndContext(
1544
+ array(nullable(SignatureStatusResponse)),
1545
+ );
1546
+
1547
+ /**
1548
+ * Expected JSON RPC response for the "getMinimumBalanceForRentExemption" message
1549
+ */
1550
+ const GetMinimumBalanceForRentExemptionRpcResult = jsonRpcResult(number());
1551
+
1552
+ const ConfirmedTransactionResult = pick({
1553
+ signatures: array(string()),
1554
+ message: pick({
1555
+ accountKeys: array(string()),
1556
+ header: pick({
1557
+ numRequiredSignatures: number(),
1558
+ numReadonlySignedAccounts: number(),
1559
+ numReadonlyUnsignedAccounts: number(),
1560
+ }),
1561
+ instructions: array(
1562
+ pick({
1563
+ accounts: array(number()),
1564
+ data: string(),
1565
+ programIdIndex: number(),
1566
+ }),
1567
+ ),
1568
+ recentBlockhash: string(),
1569
+ }),
1570
+ });
1571
+
1572
+ const ParsedInstructionResult = pick({
1573
+ parsed: unknown(),
1574
+ program: string(),
1575
+ programId: PublicKeyFromString,
1576
+ });
1577
+
1578
+ const RawInstructionResult = pick({
1579
+ accounts: array(PublicKeyFromString),
1580
+ data: string(),
1581
+ programId: PublicKeyFromString,
1582
+ });
1583
+
1584
+ const InstructionResult = union([
1585
+ RawInstructionResult,
1586
+ ParsedInstructionResult,
1587
+ ]);
1588
+
1589
+ const UnknownInstructionResult = union([
1590
+ pick({
1591
+ parsed: unknown(),
1592
+ program: string(),
1593
+ programId: string(),
1594
+ }),
1595
+ pick({
1596
+ accounts: array(string()),
1597
+ data: string(),
1598
+ programId: string(),
1599
+ }),
1600
+ ]);
1601
+
1602
+ const ParsedOrRawInstruction = coerce(
1603
+ InstructionResult,
1604
+ UnknownInstructionResult,
1605
+ value => {
1606
+ if ('accounts' in value) {
1607
+ return create(value, RawInstructionResult);
1608
+ } else {
1609
+ return create(value, ParsedInstructionResult);
1610
+ }
1611
+ },
1612
+ );
1613
+
1614
+ /**
1615
+ * @internal
1616
+ */
1617
+ const ParsedConfirmedTransactionResult = pick({
1618
+ signatures: array(string()),
1619
+ message: pick({
1620
+ accountKeys: array(
1621
+ pick({
1622
+ pubkey: PublicKeyFromString,
1623
+ signer: boolean(),
1624
+ writable: boolean(),
1625
+ }),
1626
+ ),
1627
+ instructions: array(ParsedOrRawInstruction),
1628
+ recentBlockhash: string(),
1629
+ }),
1630
+ });
1631
+
1632
+ const TokenBalanceResult = pick({
1633
+ accountIndex: number(),
1634
+ mint: string(),
1635
+ owner: optional(string()),
1636
+ uiTokenAmount: TokenAmountResult,
1637
+ });
1638
+
1639
+ /**
1640
+ * @internal
1641
+ */
1642
+ const ConfirmedTransactionMetaResult = pick({
1643
+ err: TransactionErrorResult,
1644
+ fee: number(),
1645
+ innerInstructions: optional(
1646
+ nullable(
1647
+ array(
1648
+ pick({
1649
+ index: number(),
1650
+ instructions: array(
1651
+ pick({
1652
+ accounts: array(number()),
1653
+ data: string(),
1654
+ programIdIndex: number(),
1655
+ }),
1656
+ ),
1657
+ }),
1658
+ ),
1659
+ ),
1660
+ ),
1661
+ preBalances: array(number()),
1662
+ postBalances: array(number()),
1663
+ logMessages: optional(nullable(array(string()))),
1664
+ preTokenBalances: optional(nullable(array(TokenBalanceResult))),
1665
+ postTokenBalances: optional(nullable(array(TokenBalanceResult))),
1666
+ });
1667
+
1668
+ /**
1669
+ * @internal
1670
+ */
1671
+ const ParsedConfirmedTransactionMetaResult = pick({
1672
+ err: TransactionErrorResult,
1673
+ fee: number(),
1674
+ innerInstructions: optional(
1675
+ nullable(
1676
+ array(
1677
+ pick({
1678
+ index: number(),
1679
+ instructions: array(ParsedOrRawInstruction),
1680
+ }),
1681
+ ),
1682
+ ),
1683
+ ),
1684
+ preBalances: array(number()),
1685
+ postBalances: array(number()),
1686
+ logMessages: optional(nullable(array(string()))),
1687
+ preTokenBalances: optional(nullable(array(TokenBalanceResult))),
1688
+ postTokenBalances: optional(nullable(array(TokenBalanceResult))),
1689
+ });
1690
+
1691
+ /**
1692
+ * Expected JSON RPC response for the "getBlock" message
1693
+ */
1694
+ const GetBlockRpcResult = jsonRpcResult(
1695
+ nullable(
1696
+ pick({
1697
+ blockhash: string(),
1698
+ previousBlockhash: string(),
1699
+ parentSlot: number(),
1700
+ transactions: array(
1701
+ pick({
1702
+ transaction: ConfirmedTransactionResult,
1703
+ meta: nullable(ConfirmedTransactionMetaResult),
1704
+ }),
1705
+ ),
1706
+ rewards: optional(
1707
+ array(
1708
+ pick({
1709
+ pubkey: string(),
1710
+ lamports: number(),
1711
+ postBalance: nullable(number()),
1712
+ rewardType: nullable(string()),
1713
+ }),
1714
+ ),
1715
+ ),
1716
+ blockTime: nullable(number()),
1717
+ blockHeight: nullable(number()),
1718
+ }),
1719
+ ),
1720
+ );
1721
+
1722
+ /**
1723
+ * Expected JSON RPC response for the "getConfirmedBlock" message
1724
+ *
1725
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link GetBlockRpcResult} instead.
1726
+ */
1727
+ const GetConfirmedBlockRpcResult = jsonRpcResult(
1728
+ nullable(
1729
+ pick({
1730
+ blockhash: string(),
1731
+ previousBlockhash: string(),
1732
+ parentSlot: number(),
1733
+ transactions: array(
1734
+ pick({
1735
+ transaction: ConfirmedTransactionResult,
1736
+ meta: nullable(ConfirmedTransactionMetaResult),
1737
+ }),
1738
+ ),
1739
+ rewards: optional(
1740
+ array(
1741
+ pick({
1742
+ pubkey: string(),
1743
+ lamports: number(),
1744
+ postBalance: nullable(number()),
1745
+ rewardType: nullable(string()),
1746
+ }),
1747
+ ),
1748
+ ),
1749
+ blockTime: nullable(number()),
1750
+ }),
1751
+ ),
1752
+ );
1753
+
1754
+ /**
1755
+ * Expected JSON RPC response for the "getBlock" message
1756
+ */
1757
+ const GetBlockSignaturesRpcResult = jsonRpcResult(
1758
+ nullable(
1759
+ pick({
1760
+ blockhash: string(),
1761
+ previousBlockhash: string(),
1762
+ parentSlot: number(),
1763
+ signatures: array(string()),
1764
+ blockTime: nullable(number()),
1765
+ }),
1766
+ ),
1767
+ );
1768
+
1769
+ /**
1770
+ * Expected JSON RPC response for the "getTransaction" message
1771
+ */
1772
+ const GetTransactionRpcResult = jsonRpcResult(
1773
+ nullable(
1774
+ pick({
1775
+ slot: number(),
1776
+ meta: ConfirmedTransactionMetaResult,
1777
+ blockTime: optional(nullable(number())),
1778
+ transaction: ConfirmedTransactionResult,
1779
+ }),
1780
+ ),
1781
+ );
1782
+
1783
+ /**
1784
+ * Expected parsed JSON RPC response for the "getTransaction" message
1785
+ */
1786
+ const GetParsedTransactionRpcResult = jsonRpcResult(
1787
+ nullable(
1788
+ pick({
1789
+ slot: number(),
1790
+ transaction: ParsedConfirmedTransactionResult,
1791
+ meta: nullable(ParsedConfirmedTransactionMetaResult),
1792
+ blockTime: optional(nullable(number())),
1793
+ }),
1794
+ ),
1795
+ );
1796
+
1797
+ /**
1798
+ * Expected JSON RPC response for the "getRecentBlockhash" message
1799
+ *
1800
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link GetLatestBlockhashRpcResult} instead.
1801
+ */
1802
+ const GetRecentBlockhashAndContextRpcResult = jsonRpcResultAndContext(
1803
+ pick({
1804
+ blockhash: string(),
1805
+ feeCalculator: pick({
1806
+ lamportsPerSignature: number(),
1807
+ }),
1808
+ }),
1809
+ );
1810
+
1811
+ /**
1812
+ * Expected JSON RPC response for the "getLatestBlockhash" message
1813
+ */
1814
+ const GetLatestBlockhashRpcResult = jsonRpcResultAndContext(
1815
+ pick({
1816
+ blockhash: string(),
1817
+ lastValidBlockHeight: number(),
1818
+ }),
1819
+ );
1820
+
1821
+ const PerfSampleResult = pick({
1822
+ slot: number(),
1823
+ numTransactions: number(),
1824
+ numSlots: number(),
1825
+ samplePeriodSecs: number(),
1826
+ });
1827
+
1828
+ /*
1829
+ * Expected JSON RPC response for "getRecentPerformanceSamples" message
1830
+ */
1831
+ const GetRecentPerformanceSamplesRpcResult = jsonRpcResult(
1832
+ array(PerfSampleResult),
1833
+ );
1834
+
1835
+ /**
1836
+ * Expected JSON RPC response for the "getFeeCalculatorForBlockhash" message
1837
+ */
1838
+ const GetFeeCalculatorRpcResult = jsonRpcResultAndContext(
1839
+ nullable(
1840
+ pick({
1841
+ feeCalculator: pick({
1842
+ lamportsPerSignature: number(),
1843
+ }),
1844
+ }),
1845
+ ),
1846
+ );
1847
+
1848
+ /**
1849
+ * Expected JSON RPC response for the "requestAirdrop" message
1850
+ */
1851
+ const RequestAirdropRpcResult = jsonRpcResult(string());
1852
+
1853
+ /**
1854
+ * Expected JSON RPC response for the "sendTransaction" message
1855
+ */
1856
+ const SendTransactionRpcResult = jsonRpcResult(string());
1857
+
1858
+ /**
1859
+ * Information about the latest slot being processed by a node
1860
+ */
1861
+ export type SlotInfo = {
1862
+ /** Currently processing slot */
1863
+ slot: number;
1864
+ /** Parent of the current slot */
1865
+ parent: number;
1866
+ /** The root block of the current slot's fork */
1867
+ root: number;
1868
+ };
1869
+
1870
+ /**
1871
+ * Parsed account data
1872
+ */
1873
+ export type ParsedAccountData = {
1874
+ /** Name of the program that owns this account */
1875
+ program: string;
1876
+ /** Parsed account data */
1877
+ parsed: any;
1878
+ /** Space used by account data */
1879
+ space: number;
1880
+ };
1881
+
1882
+ /**
1883
+ * Stake Activation data
1884
+ */
1885
+ export type StakeActivationData = {
1886
+ /** the stake account's activation state */
1887
+ state: 'active' | 'inactive' | 'activating' | 'deactivating';
1888
+ /** stake active during the epoch */
1889
+ active: number;
1890
+ /** stake inactive during the epoch */
1891
+ inactive: number;
1892
+ };
1893
+
1894
+ /**
1895
+ * Data slice argument for getProgramAccounts
1896
+ */
1897
+ export type DataSlice = {
1898
+ /** offset of data slice */
1899
+ offset: number;
1900
+ /** length of data slice */
1901
+ length: number;
1902
+ };
1903
+
1904
+ /**
1905
+ * Memory comparison filter for getProgramAccounts
1906
+ */
1907
+ export type MemcmpFilter = {
1908
+ memcmp: {
1909
+ /** offset into program account data to start comparison */
1910
+ offset: number;
1911
+ /** data to match, as base-58 encoded string and limited to less than 129 bytes */
1912
+ bytes: string;
1913
+ };
1914
+ };
1915
+
1916
+ /**
1917
+ * Data size comparison filter for getProgramAccounts
1918
+ */
1919
+ export type DataSizeFilter = {
1920
+ /** Size of data for program account data length comparison */
1921
+ dataSize: number;
1922
+ };
1923
+
1924
+ /**
1925
+ * A filter object for getProgramAccounts
1926
+ */
1927
+ export type GetProgramAccountsFilter = MemcmpFilter | DataSizeFilter;
1928
+
1929
+ /**
1930
+ * Configuration object for getProgramAccounts requests
1931
+ */
1932
+ export type GetProgramAccountsConfig = {
1933
+ /** Optional commitment level */
1934
+ commitment?: Commitment;
1935
+ /** Optional encoding for account data (default base64)
1936
+ * To use "jsonParsed" encoding, please refer to `getParsedProgramAccounts` in connection.ts
1937
+ * */
1938
+ encoding?: 'base64';
1939
+ /** Optional data slice to limit the returned account data */
1940
+ dataSlice?: DataSlice;
1941
+ /** Optional array of filters to apply to accounts */
1942
+ filters?: GetProgramAccountsFilter[];
1943
+ };
1944
+
1945
+ /**
1946
+ * Configuration object for getParsedProgramAccounts
1947
+ */
1948
+ export type GetParsedProgramAccountsConfig = {
1949
+ /** Optional commitment level */
1950
+ commitment?: Commitment;
1951
+ /** Optional array of filters to apply to accounts */
1952
+ filters?: GetProgramAccountsFilter[];
1953
+ };
1954
+
1955
+ /**
1956
+ * Configuration object for getMultipleAccounts
1957
+ */
1958
+ export type GetMultipleAccountsConfig = {
1959
+ /** Optional commitment level */
1960
+ commitment?: Commitment;
1961
+ /** Optional encoding for account data (default base64) */
1962
+ encoding?: 'base64' | 'jsonParsed';
1963
+ };
1964
+
1965
+ /**
1966
+ * Information describing an account
1967
+ */
1968
+ export type AccountInfo<T> = {
1969
+ /** `true` if this account's data contains a loaded program */
1970
+ executable: boolean;
1971
+ /** Identifier of the program that owns the account */
1972
+ owner: PublicKey;
1973
+ /** Number of lamports assigned to the account */
1974
+ lamports: number;
1975
+ /** Optional data assigned to the account */
1976
+ data: T;
1977
+ /** Optional rent epoch info for account */
1978
+ rentEpoch?: number;
1979
+ };
1980
+
1981
+ /**
1982
+ * Account information identified by pubkey
1983
+ */
1984
+ export type KeyedAccountInfo = {
1985
+ accountId: PublicKey;
1986
+ accountInfo: AccountInfo<Buffer>;
1987
+ };
1988
+
1989
+ /**
1990
+ * Callback function for account change notifications
1991
+ */
1992
+ export type AccountChangeCallback = (
1993
+ accountInfo: AccountInfo<Buffer>,
1994
+ context: Context,
1995
+ ) => void;
1996
+
1997
+ /**
1998
+ * Callback function for program account change notifications
1999
+ */
2000
+ export type ProgramAccountChangeCallback = (
2001
+ keyedAccountInfo: KeyedAccountInfo,
2002
+ context: Context,
2003
+ ) => void;
2004
+
2005
+ /**
2006
+ * Callback function for slot change notifications
2007
+ */
2008
+ export type SlotChangeCallback = (slotInfo: SlotInfo) => void;
2009
+
2010
+ /**
2011
+ * Callback function for slot update notifications
2012
+ */
2013
+ export type SlotUpdateCallback = (slotUpdate: SlotUpdate) => void;
2014
+
2015
+ /**
2016
+ * Callback function for signature status notifications
2017
+ */
2018
+ export type SignatureResultCallback = (
2019
+ signatureResult: SignatureResult,
2020
+ context: Context,
2021
+ ) => void;
2022
+
2023
+ /**
2024
+ * Signature status notification with transaction result
2025
+ */
2026
+ export type SignatureStatusNotification = {
2027
+ type: 'status';
2028
+ result: SignatureResult;
2029
+ };
2030
+
2031
+ /**
2032
+ * Signature received notification
2033
+ */
2034
+ export type SignatureReceivedNotification = {
2035
+ type: 'received';
2036
+ };
2037
+
2038
+ /**
2039
+ * Callback function for signature notifications
2040
+ */
2041
+ export type SignatureSubscriptionCallback = (
2042
+ notification: SignatureStatusNotification | SignatureReceivedNotification,
2043
+ context: Context,
2044
+ ) => void;
2045
+
2046
+ /**
2047
+ * Signature subscription options
2048
+ */
2049
+ export type SignatureSubscriptionOptions = {
2050
+ commitment?: Commitment; // Must default to 'finalized'
2051
+ enableReceivedNotification?: boolean;
2052
+ };
2053
+
2054
+ /**
2055
+ * Callback function for root change notifications
2056
+ */
2057
+ export type RootChangeCallback = (root: number) => void;
2058
+
2059
+ /**
2060
+ * @internal
2061
+ */
2062
+ const LogsResult = pick({
2063
+ err: TransactionErrorResult,
2064
+ logs: array(string()),
2065
+ signature: string(),
2066
+ });
2067
+
2068
+ /**
2069
+ * Logs result.
2070
+ */
2071
+ export type Logs = {
2072
+ err: TransactionError | null;
2073
+ logs: string[];
2074
+ signature: string;
2075
+ };
2076
+
2077
+ /**
2078
+ * Expected JSON RPC response for the "logsNotification" message.
2079
+ */
2080
+ const LogsNotificationResult = pick({
2081
+ result: notificationResultAndContext(LogsResult),
2082
+ subscription: number(),
2083
+ });
2084
+
2085
+ /**
2086
+ * Filter for log subscriptions.
2087
+ */
2088
+ export type LogsFilter = PublicKey | 'all' | 'allWithVotes';
2089
+
2090
+ /**
2091
+ * Callback function for log notifications.
2092
+ */
2093
+ export type LogsCallback = (logs: Logs, ctx: Context) => void;
2094
+
2095
+ /**
2096
+ * Signature result
2097
+ */
2098
+ export type SignatureResult = {
2099
+ err: TransactionError | null;
2100
+ };
2101
+
2102
+ /**
2103
+ * Transaction error
2104
+ */
2105
+ export type TransactionError = {} | string;
2106
+
2107
+ /**
2108
+ * Transaction confirmation status
2109
+ * <pre>
2110
+ * 'processed': Transaction landed in a block which has reached 1 confirmation by the connected node
2111
+ * 'confirmed': Transaction landed in a block which has reached 1 confirmation by the cluster
2112
+ * 'finalized': Transaction landed in a block which has been finalized by the cluster
2113
+ * </pre>
2114
+ */
2115
+ export type TransactionConfirmationStatus =
2116
+ | 'processed'
2117
+ | 'confirmed'
2118
+ | 'finalized';
2119
+
2120
+ /**
2121
+ * Signature status
2122
+ */
2123
+ export type SignatureStatus = {
2124
+ /** when the transaction was processed */
2125
+ slot: number;
2126
+ /** the number of blocks that have been confirmed and voted on in the fork containing `slot` */
2127
+ confirmations: number | null;
2128
+ /** transaction error, if any */
2129
+ err: TransactionError | null;
2130
+ /** cluster confirmation status, if data available. Possible responses: `processed`, `confirmed`, `finalized` */
2131
+ confirmationStatus?: TransactionConfirmationStatus;
2132
+ };
2133
+
2134
+ /**
2135
+ * A confirmed signature with its status
2136
+ */
2137
+ export type ConfirmedSignatureInfo = {
2138
+ /** the transaction signature */
2139
+ signature: string;
2140
+ /** when the transaction was processed */
2141
+ slot: number;
2142
+ /** error, if any */
2143
+ err: TransactionError | null;
2144
+ /** memo associated with the transaction, if any */
2145
+ memo: string | null;
2146
+ /** The unix timestamp of when the transaction was processed */
2147
+ blockTime?: number | null;
2148
+ };
2149
+
2150
+ /**
2151
+ * An object defining headers to be passed to the RPC server
2152
+ */
2153
+ export type HttpHeaders = {[header: string]: string};
2154
+
2155
+ /**
2156
+ * A callback used to augment the outgoing HTTP request
2157
+ */
2158
+ export type FetchMiddleware = (
2159
+ url: string,
2160
+ options: any,
2161
+ fetch: (modifiedUrl: string, modifiedOptions: any) => void,
2162
+ ) => void;
2163
+
2164
+ /**
2165
+ * Configuration for instantiating a Connection
2166
+ */
2167
+ export type ConnectionConfig = {
2168
+ /** Optional commitment level */
2169
+ commitment?: Commitment;
2170
+ /** Optional endpoint URL to the fullnode JSON RPC PubSub WebSocket Endpoint */
2171
+ wsEndpoint?: string;
2172
+ /** Optional HTTP headers object */
2173
+ httpHeaders?: HttpHeaders;
2174
+ /** Optional custom fetch function */
2175
+ fetch?: typeof crossFetch;
2176
+ /** Optional fetch middleware callback */
2177
+ fetchMiddleware?: FetchMiddleware;
2178
+ /** Optional Disable retrying calls when server responds with HTTP 429 (Too Many Requests) */
2179
+ disableRetryOnRateLimit?: boolean;
2180
+ /** time to allow for the server to initially process a transaction (in milliseconds) */
2181
+ confirmTransactionInitialTimeout?: number;
2182
+ };
2183
+
2184
+ /**
2185
+ * A connection to a fullnode JSON RPC endpoint
2186
+ */
2187
+ export class Connection {
2188
+ /** @internal */ _commitment?: Commitment;
2189
+ /** @internal */ _confirmTransactionInitialTimeout?: number;
2190
+ /** @internal */ _rpcEndpoint: string;
2191
+ /** @internal */ _rpcWsEndpoint: string;
2192
+ /** @internal */ _rpcClient: RpcClient;
2193
+ /** @internal */ _rpcRequest: RpcRequest;
2194
+ /** @internal */ _rpcBatchRequest: RpcBatchRequest;
2195
+ /** @internal */ _rpcWebSocket: RpcWebSocketClient;
2196
+ /** @internal */ _rpcWebSocketConnected: boolean = false;
2197
+ /** @internal */ _rpcWebSocketHeartbeat: ReturnType<
2198
+ typeof setInterval
2199
+ > | null = null;
2200
+ /** @internal */ _rpcWebSocketIdleTimeout: ReturnType<
2201
+ typeof setTimeout
2202
+ > | null = null;
2203
+
2204
+ /** @internal */ _disableBlockhashCaching: boolean = false;
2205
+ /** @internal */ _pollingBlockhash: boolean = false;
2206
+ /** @internal */ _blockhashInfo: {
2207
+ recentBlockhash: Blockhash | null;
2208
+ lastFetch: number;
2209
+ simulatedSignatures: Array<string>;
2210
+ transactionSignatures: Array<string>;
2211
+ } = {
2212
+ recentBlockhash: null,
2213
+ lastFetch: 0,
2214
+ transactionSignatures: [],
2215
+ simulatedSignatures: [],
2216
+ };
2217
+
2218
+ /** @internal */ _nextClientSubscriptionId: ClientSubscriptionId = 0;
2219
+ /** @internal */ _subscriptionDisposeFunctionsByClientSubscriptionId: {
2220
+ [clientSubscriptionId: ClientSubscriptionId]:
2221
+ | SubscriptionDisposeFn
2222
+ | undefined;
2223
+ } = {};
2224
+ /** @internal */ _subscriptionCallbacksByServerSubscriptionId: {
2225
+ [serverSubscriptionId: ServerSubscriptionId]:
2226
+ | Set<SubscriptionConfig['callback']>
2227
+ | undefined;
2228
+ } = {};
2229
+ /** @internal */ _subscriptionsByHash: {
2230
+ [hash: SubscriptionConfigHash]: Subscription | undefined;
2231
+ } = {};
2232
+ /**
2233
+ * Special case.
2234
+ * After a signature is processed, RPCs automatically dispose of the
2235
+ * subscription on the server side. We need to track which of these
2236
+ * subscriptions have been disposed in such a way, so that we know
2237
+ * whether the client is dealing with a not-yet-processed signature
2238
+ * (in which case we must tear down the server subscription) or an
2239
+ * already-processed signature (in which case the client can simply
2240
+ * clear out the subscription locally without telling the server).
2241
+ *
2242
+ * NOTE: There is a proposal to eliminate this special case, here:
2243
+ * https://github.com/solana-labs/solana/issues/18892
2244
+ */
2245
+ /** @internal */ _subscriptionsAutoDisposedByRpc: Set<ServerSubscriptionId> =
2246
+ new Set();
2247
+
2248
+ /**
2249
+ * Establish a JSON RPC connection
2250
+ *
2251
+ * @param endpoint URL to the fullnode JSON RPC endpoint
2252
+ * @param commitmentOrConfig optional default commitment level or optional ConnectionConfig configuration object
2253
+ */
2254
+ constructor(
2255
+ endpoint: string,
2256
+ commitmentOrConfig?: Commitment | ConnectionConfig,
2257
+ ) {
2258
+ let url = new URL(endpoint);
2259
+ const useHttps = url.protocol === 'https:';
2260
+
2261
+ let wsEndpoint;
2262
+ let httpHeaders;
2263
+ let fetch;
2264
+ let fetchMiddleware;
2265
+ let disableRetryOnRateLimit;
2266
+ if (commitmentOrConfig && typeof commitmentOrConfig === 'string') {
2267
+ this._commitment = commitmentOrConfig;
2268
+ } else if (commitmentOrConfig) {
2269
+ this._commitment = commitmentOrConfig.commitment;
2270
+ this._confirmTransactionInitialTimeout =
2271
+ commitmentOrConfig.confirmTransactionInitialTimeout;
2272
+ wsEndpoint = commitmentOrConfig.wsEndpoint;
2273
+ httpHeaders = commitmentOrConfig.httpHeaders;
2274
+ fetch = commitmentOrConfig.fetch;
2275
+ fetchMiddleware = commitmentOrConfig.fetchMiddleware;
2276
+ disableRetryOnRateLimit = commitmentOrConfig.disableRetryOnRateLimit;
2277
+ }
2278
+
2279
+ this._rpcEndpoint = endpoint;
2280
+ this._rpcWsEndpoint = wsEndpoint || makeWebsocketUrl(endpoint);
2281
+
2282
+ this._rpcClient = createRpcClient(
2283
+ url.toString(),
2284
+ useHttps,
2285
+ httpHeaders,
2286
+ fetch,
2287
+ fetchMiddleware,
2288
+ disableRetryOnRateLimit,
2289
+ );
2290
+ this._rpcRequest = createRpcRequest(this._rpcClient);
2291
+ this._rpcBatchRequest = createRpcBatchRequest(this._rpcClient);
2292
+
2293
+ this._rpcWebSocket = new RpcWebSocketClient(this._rpcWsEndpoint, {
2294
+ autoconnect: false,
2295
+ max_reconnects: Infinity,
2296
+ });
2297
+ this._rpcWebSocket.on('open', this._wsOnOpen.bind(this));
2298
+ this._rpcWebSocket.on('error', this._wsOnError.bind(this));
2299
+ this._rpcWebSocket.on('close', this._wsOnClose.bind(this));
2300
+ this._rpcWebSocket.on(
2301
+ 'accountNotification',
2302
+ this._wsOnAccountNotification.bind(this),
2303
+ );
2304
+ this._rpcWebSocket.on(
2305
+ 'programNotification',
2306
+ this._wsOnProgramAccountNotification.bind(this),
2307
+ );
2308
+ this._rpcWebSocket.on(
2309
+ 'slotNotification',
2310
+ this._wsOnSlotNotification.bind(this),
2311
+ );
2312
+ this._rpcWebSocket.on(
2313
+ 'slotsUpdatesNotification',
2314
+ this._wsOnSlotUpdatesNotification.bind(this),
2315
+ );
2316
+ this._rpcWebSocket.on(
2317
+ 'signatureNotification',
2318
+ this._wsOnSignatureNotification.bind(this),
2319
+ );
2320
+ this._rpcWebSocket.on(
2321
+ 'rootNotification',
2322
+ this._wsOnRootNotification.bind(this),
2323
+ );
2324
+ this._rpcWebSocket.on(
2325
+ 'logsNotification',
2326
+ this._wsOnLogsNotification.bind(this),
2327
+ );
2328
+ }
2329
+
2330
+ /**
2331
+ * The default commitment used for requests
2332
+ */
2333
+ get commitment(): Commitment | undefined {
2334
+ return this._commitment;
2335
+ }
2336
+
2337
+ /**
2338
+ * The RPC endpoint
2339
+ */
2340
+ get rpcEndpoint(): string {
2341
+ return this._rpcEndpoint;
2342
+ }
2343
+
2344
+ /**
2345
+ * Fetch the balance for the specified public key, return with context
2346
+ */
2347
+ async getBalanceAndContext(
2348
+ publicKey: PublicKey,
2349
+ commitment?: Commitment,
2350
+ ): Promise<RpcResponseAndContext<number>> {
2351
+ const args = this._buildArgs([publicKey.toBase58()], commitment);
2352
+ const unsafeRes = await this._rpcRequest('getBalance', args);
2353
+ const res = create(unsafeRes, jsonRpcResultAndContext(number()));
2354
+ if ('error' in res) {
2355
+ throw new Error(
2356
+ 'failed to get balance for ' +
2357
+ publicKey.toBase58() +
2358
+ ': ' +
2359
+ res.error.message,
2360
+ );
2361
+ }
2362
+ return res.result;
2363
+ }
2364
+
2365
+ /**
2366
+ * Fetch the balance for the specified public key
2367
+ */
2368
+ async getBalance(
2369
+ publicKey: PublicKey,
2370
+ commitment?: Commitment,
2371
+ ): Promise<number> {
2372
+ return await this.getBalanceAndContext(publicKey, commitment)
2373
+ .then(x => x.value)
2374
+ .catch(e => {
2375
+ throw new Error(
2376
+ 'failed to get balance of account ' + publicKey.toBase58() + ': ' + e,
2377
+ );
2378
+ });
2379
+ }
2380
+
2381
+ /**
2382
+ * Fetch the estimated production time of a block
2383
+ */
2384
+ async getBlockTime(slot: number): Promise<number | null> {
2385
+ const unsafeRes = await this._rpcRequest('getBlockTime', [slot]);
2386
+ const res = create(unsafeRes, jsonRpcResult(nullable(number())));
2387
+ if ('error' in res) {
2388
+ throw new Error(
2389
+ 'failed to get block time for slot ' + slot + ': ' + res.error.message,
2390
+ );
2391
+ }
2392
+ return res.result;
2393
+ }
2394
+
2395
+ /**
2396
+ * Fetch the lowest slot that the node has information about in its ledger.
2397
+ * This value may increase over time if the node is configured to purge older ledger data
2398
+ */
2399
+ async getMinimumLedgerSlot(): Promise<number> {
2400
+ const unsafeRes = await this._rpcRequest('minimumLedgerSlot', []);
2401
+ const res = create(unsafeRes, jsonRpcResult(number()));
2402
+ if ('error' in res) {
2403
+ throw new Error(
2404
+ 'failed to get minimum ledger slot: ' + res.error.message,
2405
+ );
2406
+ }
2407
+ return res.result;
2408
+ }
2409
+
2410
+ /**
2411
+ * Fetch the slot of the lowest confirmed block that has not been purged from the ledger
2412
+ */
2413
+ async getFirstAvailableBlock(): Promise<number> {
2414
+ const unsafeRes = await this._rpcRequest('getFirstAvailableBlock', []);
2415
+ const res = create(unsafeRes, SlotRpcResult);
2416
+ if ('error' in res) {
2417
+ throw new Error(
2418
+ 'failed to get first available block: ' + res.error.message,
2419
+ );
2420
+ }
2421
+ return res.result;
2422
+ }
2423
+
2424
+ /**
2425
+ * Fetch information about the current supply
2426
+ */
2427
+ async getSupply(
2428
+ config?: GetSupplyConfig | Commitment,
2429
+ ): Promise<RpcResponseAndContext<Supply>> {
2430
+ let configArg: GetSupplyConfig = {};
2431
+ if (typeof config === 'string') {
2432
+ configArg = {commitment: config};
2433
+ } else if (config) {
2434
+ configArg = {
2435
+ ...config,
2436
+ commitment: (config && config.commitment) || this.commitment,
2437
+ };
2438
+ } else {
2439
+ configArg = {
2440
+ commitment: this.commitment,
2441
+ };
2442
+ }
2443
+
2444
+ const unsafeRes = await this._rpcRequest('getSupply', [configArg]);
2445
+ const res = create(unsafeRes, GetSupplyRpcResult);
2446
+ if ('error' in res) {
2447
+ throw new Error('failed to get supply: ' + res.error.message);
2448
+ }
2449
+ return res.result;
2450
+ }
2451
+
2452
+ /**
2453
+ * Fetch the current supply of a token mint
2454
+ */
2455
+ async getTokenSupply(
2456
+ tokenMintAddress: PublicKey,
2457
+ commitment?: Commitment,
2458
+ ): Promise<RpcResponseAndContext<TokenAmount>> {
2459
+ const args = this._buildArgs([tokenMintAddress.toBase58()], commitment);
2460
+ const unsafeRes = await this._rpcRequest('getTokenSupply', args);
2461
+ const res = create(unsafeRes, jsonRpcResultAndContext(TokenAmountResult));
2462
+ if ('error' in res) {
2463
+ throw new Error('failed to get token supply: ' + res.error.message);
2464
+ }
2465
+ return res.result;
2466
+ }
2467
+
2468
+ /**
2469
+ * Fetch the current balance of a token account
2470
+ */
2471
+ async getTokenAccountBalance(
2472
+ tokenAddress: PublicKey,
2473
+ commitment?: Commitment,
2474
+ ): Promise<RpcResponseAndContext<TokenAmount>> {
2475
+ const args = this._buildArgs([tokenAddress.toBase58()], commitment);
2476
+ const unsafeRes = await this._rpcRequest('getTokenAccountBalance', args);
2477
+ const res = create(unsafeRes, jsonRpcResultAndContext(TokenAmountResult));
2478
+ if ('error' in res) {
2479
+ throw new Error(
2480
+ 'failed to get token account balance: ' + res.error.message,
2481
+ );
2482
+ }
2483
+ return res.result;
2484
+ }
2485
+
2486
+ /**
2487
+ * Fetch all the token accounts owned by the specified account
2488
+ *
2489
+ * @return {Promise<RpcResponseAndContext<Array<{pubkey: PublicKey, account: AccountInfo<Buffer>}>>>}
2490
+ */
2491
+ async getTokenAccountsByOwner(
2492
+ ownerAddress: PublicKey,
2493
+ filter: TokenAccountsFilter,
2494
+ commitment?: Commitment,
2495
+ ): Promise<
2496
+ RpcResponseAndContext<
2497
+ Array<{pubkey: PublicKey; account: AccountInfo<Buffer>}>
2498
+ >
2499
+ > {
2500
+ let _args: any[] = [ownerAddress.toBase58()];
2501
+ if ('mint' in filter) {
2502
+ _args.push({mint: filter.mint.toBase58()});
2503
+ } else {
2504
+ _args.push({programId: filter.programId.toBase58()});
2505
+ }
2506
+
2507
+ const args = this._buildArgs(_args, commitment, 'base64');
2508
+ const unsafeRes = await this._rpcRequest('getTokenAccountsByOwner', args);
2509
+ const res = create(unsafeRes, GetTokenAccountsByOwner);
2510
+ if ('error' in res) {
2511
+ throw new Error(
2512
+ 'failed to get token accounts owned by account ' +
2513
+ ownerAddress.toBase58() +
2514
+ ': ' +
2515
+ res.error.message,
2516
+ );
2517
+ }
2518
+ return res.result;
2519
+ }
2520
+
2521
+ /**
2522
+ * Fetch parsed token accounts owned by the specified account
2523
+ *
2524
+ * @return {Promise<RpcResponseAndContext<Array<{pubkey: PublicKey, account: AccountInfo<ParsedAccountData>}>>>}
2525
+ */
2526
+ async getParsedTokenAccountsByOwner(
2527
+ ownerAddress: PublicKey,
2528
+ filter: TokenAccountsFilter,
2529
+ commitment?: Commitment,
2530
+ ): Promise<
2531
+ RpcResponseAndContext<
2532
+ Array<{pubkey: PublicKey; account: AccountInfo<ParsedAccountData>}>
2533
+ >
2534
+ > {
2535
+ let _args: any[] = [ownerAddress.toBase58()];
2536
+ if ('mint' in filter) {
2537
+ _args.push({mint: filter.mint.toBase58()});
2538
+ } else {
2539
+ _args.push({programId: filter.programId.toBase58()});
2540
+ }
2541
+
2542
+ const args = this._buildArgs(_args, commitment, 'jsonParsed');
2543
+ const unsafeRes = await this._rpcRequest('getTokenAccountsByOwner', args);
2544
+ const res = create(unsafeRes, GetParsedTokenAccountsByOwner);
2545
+ if ('error' in res) {
2546
+ throw new Error(
2547
+ 'failed to get token accounts owned by account ' +
2548
+ ownerAddress.toBase58() +
2549
+ ': ' +
2550
+ res.error.message,
2551
+ );
2552
+ }
2553
+ return res.result;
2554
+ }
2555
+
2556
+ /**
2557
+ * Fetch the 20 largest accounts with their current balances
2558
+ */
2559
+ async getLargestAccounts(
2560
+ config?: GetLargestAccountsConfig,
2561
+ ): Promise<RpcResponseAndContext<Array<AccountBalancePair>>> {
2562
+ const arg = {
2563
+ ...config,
2564
+ commitment: (config && config.commitment) || this.commitment,
2565
+ };
2566
+ const args = arg.filter || arg.commitment ? [arg] : [];
2567
+ const unsafeRes = await this._rpcRequest('getLargestAccounts', args);
2568
+ const res = create(unsafeRes, GetLargestAccountsRpcResult);
2569
+ if ('error' in res) {
2570
+ throw new Error('failed to get largest accounts: ' + res.error.message);
2571
+ }
2572
+ return res.result;
2573
+ }
2574
+
2575
+ /**
2576
+ * Fetch the 20 largest token accounts with their current balances
2577
+ * for a given mint.
2578
+ */
2579
+ async getTokenLargestAccounts(
2580
+ mintAddress: PublicKey,
2581
+ commitment?: Commitment,
2582
+ ): Promise<RpcResponseAndContext<Array<TokenAccountBalancePair>>> {
2583
+ const args = this._buildArgs([mintAddress.toBase58()], commitment);
2584
+ const unsafeRes = await this._rpcRequest('getTokenLargestAccounts', args);
2585
+ const res = create(unsafeRes, GetTokenLargestAccountsResult);
2586
+ if ('error' in res) {
2587
+ throw new Error(
2588
+ 'failed to get token largest accounts: ' + res.error.message,
2589
+ );
2590
+ }
2591
+ return res.result;
2592
+ }
2593
+
2594
+ /**
2595
+ * Fetch all the account info for the specified public key, return with context
2596
+ */
2597
+ async getAccountInfoAndContext(
2598
+ publicKey: PublicKey,
2599
+ commitment?: Commitment,
2600
+ ): Promise<RpcResponseAndContext<AccountInfo<Buffer> | null>> {
2601
+ const args = this._buildArgs([publicKey.toBase58()], commitment, 'base64');
2602
+ const unsafeRes = await this._rpcRequest('getAccountInfo', args);
2603
+ const res = create(
2604
+ unsafeRes,
2605
+ jsonRpcResultAndContext(nullable(AccountInfoResult)),
2606
+ );
2607
+ if ('error' in res) {
2608
+ throw new Error(
2609
+ 'failed to get info about account ' +
2610
+ publicKey.toBase58() +
2611
+ ': ' +
2612
+ res.error.message,
2613
+ );
2614
+ }
2615
+ return res.result;
2616
+ }
2617
+
2618
+ /**
2619
+ * Fetch parsed account info for the specified public key
2620
+ */
2621
+ async getParsedAccountInfo(
2622
+ publicKey: PublicKey,
2623
+ commitment?: Commitment,
2624
+ ): Promise<
2625
+ RpcResponseAndContext<AccountInfo<Buffer | ParsedAccountData> | null>
2626
+ > {
2627
+ const args = this._buildArgs(
2628
+ [publicKey.toBase58()],
2629
+ commitment,
2630
+ 'jsonParsed',
2631
+ );
2632
+ const unsafeRes = await this._rpcRequest('getAccountInfo', args);
2633
+ const res = create(
2634
+ unsafeRes,
2635
+ jsonRpcResultAndContext(nullable(ParsedAccountInfoResult)),
2636
+ );
2637
+ if ('error' in res) {
2638
+ throw new Error(
2639
+ 'failed to get info about account ' +
2640
+ publicKey.toBase58() +
2641
+ ': ' +
2642
+ res.error.message,
2643
+ );
2644
+ }
2645
+ return res.result;
2646
+ }
2647
+
2648
+ /**
2649
+ * Fetch all the account info for the specified public key
2650
+ */
2651
+ async getAccountInfo(
2652
+ publicKey: PublicKey,
2653
+ commitment?: Commitment,
2654
+ ): Promise<AccountInfo<Buffer> | null> {
2655
+ try {
2656
+ const res = await this.getAccountInfoAndContext(publicKey, commitment);
2657
+ return res.value;
2658
+ } catch (e) {
2659
+ throw new Error(
2660
+ 'failed to get info about account ' + publicKey.toBase58() + ': ' + e,
2661
+ );
2662
+ }
2663
+ }
2664
+
2665
+ /**
2666
+ * Fetch all the account info for multiple accounts specified by an array of public keys, return with context
2667
+ */
2668
+ async getMultipleAccountsInfoAndContext(
2669
+ publicKeys: PublicKey[],
2670
+ commitment?: Commitment,
2671
+ ): Promise<RpcResponseAndContext<(AccountInfo<Buffer> | null)[]>> {
2672
+ const keys = publicKeys.map(key => key.toBase58());
2673
+ const args = this._buildArgs([keys], commitment, 'base64');
2674
+ const unsafeRes = await this._rpcRequest('getMultipleAccounts', args);
2675
+ const res = create(
2676
+ unsafeRes,
2677
+ jsonRpcResultAndContext(array(nullable(AccountInfoResult))),
2678
+ );
2679
+ if ('error' in res) {
2680
+ throw new Error(
2681
+ 'failed to get info for accounts ' + keys + ': ' + res.error.message,
2682
+ );
2683
+ }
2684
+ return res.result;
2685
+ }
2686
+
2687
+ /**
2688
+ * Fetch all the account info for multiple accounts specified by an array of public keys
2689
+ */
2690
+ async getMultipleAccountsInfo(
2691
+ publicKeys: PublicKey[],
2692
+ commitment?: Commitment,
2693
+ ): Promise<(AccountInfo<Buffer> | null)[]> {
2694
+ const res = await this.getMultipleAccountsInfoAndContext(
2695
+ publicKeys,
2696
+ commitment,
2697
+ );
2698
+ return res.value;
2699
+ }
2700
+
2701
+ /**
2702
+ * Returns epoch activation information for a stake account that has been delegated
2703
+ */
2704
+ async getStakeActivation(
2705
+ publicKey: PublicKey,
2706
+ commitment?: Commitment,
2707
+ epoch?: number,
2708
+ ): Promise<StakeActivationData> {
2709
+ const args = this._buildArgs(
2710
+ [publicKey.toBase58()],
2711
+ commitment,
2712
+ undefined,
2713
+ epoch !== undefined ? {epoch} : undefined,
2714
+ );
2715
+
2716
+ const unsafeRes = await this._rpcRequest('getStakeActivation', args);
2717
+ const res = create(unsafeRes, jsonRpcResult(StakeActivationResult));
2718
+ if ('error' in res) {
2719
+ throw new Error(
2720
+ `failed to get Stake Activation ${publicKey.toBase58()}: ${
2721
+ res.error.message
2722
+ }`,
2723
+ );
2724
+ }
2725
+ return res.result;
2726
+ }
2727
+
2728
+ /**
2729
+ * Fetch all the accounts owned by the specified program id
2730
+ *
2731
+ * @return {Promise<Array<{pubkey: PublicKey, account: AccountInfo<Buffer>}>>}
2732
+ */
2733
+ async getProgramAccounts(
2734
+ programId: PublicKey,
2735
+ configOrCommitment?: GetProgramAccountsConfig | Commitment,
2736
+ ): Promise<Array<{pubkey: PublicKey; account: AccountInfo<Buffer>}>> {
2737
+ const extra: Pick<GetProgramAccountsConfig, 'dataSlice' | 'filters'> = {};
2738
+
2739
+ let commitment;
2740
+ let encoding;
2741
+ if (configOrCommitment) {
2742
+ if (typeof configOrCommitment === 'string') {
2743
+ commitment = configOrCommitment;
2744
+ } else {
2745
+ commitment = configOrCommitment.commitment;
2746
+ encoding = configOrCommitment.encoding;
2747
+
2748
+ if (configOrCommitment.dataSlice) {
2749
+ extra.dataSlice = configOrCommitment.dataSlice;
2750
+ }
2751
+ if (configOrCommitment.filters) {
2752
+ extra.filters = configOrCommitment.filters;
2753
+ }
2754
+ }
2755
+ }
2756
+
2757
+ const args = this._buildArgs(
2758
+ [programId.toBase58()],
2759
+ commitment,
2760
+ encoding || 'base64',
2761
+ extra,
2762
+ );
2763
+ const unsafeRes = await this._rpcRequest('getProgramAccounts', args);
2764
+ const res = create(unsafeRes, jsonRpcResult(array(KeyedAccountInfoResult)));
2765
+ if ('error' in res) {
2766
+ throw new Error(
2767
+ 'failed to get accounts owned by program ' +
2768
+ programId.toBase58() +
2769
+ ': ' +
2770
+ res.error.message,
2771
+ );
2772
+ }
2773
+ return res.result;
2774
+ }
2775
+
2776
+ /**
2777
+ * Fetch and parse all the accounts owned by the specified program id
2778
+ *
2779
+ * @return {Promise<Array<{pubkey: PublicKey, account: AccountInfo<Buffer | ParsedAccountData>}>>}
2780
+ */
2781
+ async getParsedProgramAccounts(
2782
+ programId: PublicKey,
2783
+ configOrCommitment?: GetParsedProgramAccountsConfig | Commitment,
2784
+ ): Promise<
2785
+ Array<{
2786
+ pubkey: PublicKey;
2787
+ account: AccountInfo<Buffer | ParsedAccountData>;
2788
+ }>
2789
+ > {
2790
+ const extra: Pick<GetParsedProgramAccountsConfig, 'filters'> = {};
2791
+
2792
+ let commitment;
2793
+ if (configOrCommitment) {
2794
+ if (typeof configOrCommitment === 'string') {
2795
+ commitment = configOrCommitment;
2796
+ } else {
2797
+ commitment = configOrCommitment.commitment;
2798
+
2799
+ if (configOrCommitment.filters) {
2800
+ extra.filters = configOrCommitment.filters;
2801
+ }
2802
+ }
2803
+ }
2804
+
2805
+ const args = this._buildArgs(
2806
+ [programId.toBase58()],
2807
+ commitment,
2808
+ 'jsonParsed',
2809
+ extra,
2810
+ );
2811
+ const unsafeRes = await this._rpcRequest('getProgramAccounts', args);
2812
+ const res = create(
2813
+ unsafeRes,
2814
+ jsonRpcResult(array(KeyedParsedAccountInfoResult)),
2815
+ );
2816
+ if ('error' in res) {
2817
+ throw new Error(
2818
+ 'failed to get accounts owned by program ' +
2819
+ programId.toBase58() +
2820
+ ': ' +
2821
+ res.error.message,
2822
+ );
2823
+ }
2824
+ return res.result;
2825
+ }
2826
+
2827
+ /**
2828
+ * Confirm the transaction identified by the specified signature.
2829
+ */
2830
+ async confirmTransaction(
2831
+ signature: TransactionSignature,
2832
+ commitment?: Commitment,
2833
+ ): Promise<RpcResponseAndContext<SignatureResult>> {
2834
+ let decodedSignature;
2835
+ try {
2836
+ decodedSignature = bs58.decode(signature);
2837
+ } catch (err) {
2838
+ throw new Error('signature must be base58 encoded: ' + signature);
2839
+ }
2840
+
2841
+ assert(decodedSignature.length === 64, 'signature has invalid length');
2842
+
2843
+ const start = Date.now();
2844
+ const subscriptionCommitment = commitment || this.commitment;
2845
+
2846
+ let subscriptionId;
2847
+ let response: RpcResponseAndContext<SignatureResult> | null = null;
2848
+ const confirmPromise = new Promise((resolve, reject) => {
2849
+ try {
2850
+ subscriptionId = this.onSignature(
2851
+ signature,
2852
+ (result: SignatureResult, context: Context) => {
2853
+ subscriptionId = undefined;
2854
+ response = {
2855
+ context,
2856
+ value: result,
2857
+ };
2858
+ resolve(null);
2859
+ },
2860
+ subscriptionCommitment,
2861
+ );
2862
+ } catch (err) {
2863
+ reject(err);
2864
+ }
2865
+ });
2866
+
2867
+ let timeoutMs = this._confirmTransactionInitialTimeout || 60 * 1000;
2868
+ switch (subscriptionCommitment) {
2869
+ case 'processed':
2870
+ case 'recent':
2871
+ case 'single':
2872
+ case 'confirmed':
2873
+ case 'singleGossip': {
2874
+ timeoutMs = this._confirmTransactionInitialTimeout || 30 * 1000;
2875
+ break;
2876
+ }
2877
+ // exhaust enums to ensure full coverage
2878
+ case 'finalized':
2879
+ case 'max':
2880
+ case 'root':
2881
+ }
2882
+
2883
+ try {
2884
+ await promiseTimeout(confirmPromise, timeoutMs);
2885
+ } finally {
2886
+ if (subscriptionId) {
2887
+ this.removeSignatureListener(subscriptionId);
2888
+ }
2889
+ }
2890
+
2891
+ if (response === null) {
2892
+ const duration = (Date.now() - start) / 1000;
2893
+ throw new Error(
2894
+ `Transaction was not confirmed in ${duration.toFixed(
2895
+ 2,
2896
+ )} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`,
2897
+ );
2898
+ }
2899
+
2900
+ return response;
2901
+ }
2902
+
2903
+ /**
2904
+ * Return the list of nodes that are currently participating in the cluster
2905
+ */
2906
+ async getClusterNodes(): Promise<Array<ContactInfo>> {
2907
+ const unsafeRes = await this._rpcRequest('getClusterNodes', []);
2908
+ const res = create(unsafeRes, jsonRpcResult(array(ContactInfoResult)));
2909
+ if ('error' in res) {
2910
+ throw new Error('failed to get cluster nodes: ' + res.error.message);
2911
+ }
2912
+ return res.result;
2913
+ }
2914
+
2915
+ /**
2916
+ * Return the list of nodes that are currently participating in the cluster
2917
+ */
2918
+ async getVoteAccounts(commitment?: Commitment): Promise<VoteAccountStatus> {
2919
+ const args = this._buildArgs([], commitment);
2920
+ const unsafeRes = await this._rpcRequest('getVoteAccounts', args);
2921
+ const res = create(unsafeRes, GetVoteAccounts);
2922
+ if ('error' in res) {
2923
+ throw new Error('failed to get vote accounts: ' + res.error.message);
2924
+ }
2925
+ return res.result;
2926
+ }
2927
+
2928
+ /**
2929
+ * Fetch the current slot that the node is processing
2930
+ */
2931
+ async getSlot(commitment?: Commitment): Promise<number> {
2932
+ const args = this._buildArgs([], commitment);
2933
+ const unsafeRes = await this._rpcRequest('getSlot', args);
2934
+ const res = create(unsafeRes, jsonRpcResult(number()));
2935
+ if ('error' in res) {
2936
+ throw new Error('failed to get slot: ' + res.error.message);
2937
+ }
2938
+ return res.result;
2939
+ }
2940
+
2941
+ /**
2942
+ * Fetch the current slot leader of the cluster
2943
+ */
2944
+ async getSlotLeader(commitment?: Commitment): Promise<string> {
2945
+ const args = this._buildArgs([], commitment);
2946
+ const unsafeRes = await this._rpcRequest('getSlotLeader', args);
2947
+ const res = create(unsafeRes, jsonRpcResult(string()));
2948
+ if ('error' in res) {
2949
+ throw new Error('failed to get slot leader: ' + res.error.message);
2950
+ }
2951
+ return res.result;
2952
+ }
2953
+
2954
+ /**
2955
+ * Fetch `limit` number of slot leaders starting from `startSlot`
2956
+ *
2957
+ * @param startSlot fetch slot leaders starting from this slot
2958
+ * @param limit number of slot leaders to return
2959
+ */
2960
+ async getSlotLeaders(
2961
+ startSlot: number,
2962
+ limit: number,
2963
+ ): Promise<Array<PublicKey>> {
2964
+ const args = [startSlot, limit];
2965
+ const unsafeRes = await this._rpcRequest('getSlotLeaders', args);
2966
+ const res = create(unsafeRes, jsonRpcResult(array(PublicKeyFromString)));
2967
+ if ('error' in res) {
2968
+ throw new Error('failed to get slot leaders: ' + res.error.message);
2969
+ }
2970
+ return res.result;
2971
+ }
2972
+
2973
+ /**
2974
+ * Fetch the current status of a signature
2975
+ */
2976
+ async getSignatureStatus(
2977
+ signature: TransactionSignature,
2978
+ config?: SignatureStatusConfig,
2979
+ ): Promise<RpcResponseAndContext<SignatureStatus | null>> {
2980
+ const {context, value: values} = await this.getSignatureStatuses(
2981
+ [signature],
2982
+ config,
2983
+ );
2984
+ assert(values.length === 1);
2985
+ const value = values[0];
2986
+ return {context, value};
2987
+ }
2988
+
2989
+ /**
2990
+ * Fetch the current statuses of a batch of signatures
2991
+ */
2992
+ async getSignatureStatuses(
2993
+ signatures: Array<TransactionSignature>,
2994
+ config?: SignatureStatusConfig,
2995
+ ): Promise<RpcResponseAndContext<Array<SignatureStatus | null>>> {
2996
+ const params: any[] = [signatures];
2997
+ if (config) {
2998
+ params.push(config);
2999
+ }
3000
+ const unsafeRes = await this._rpcRequest('getSignatureStatuses', params);
3001
+ const res = create(unsafeRes, GetSignatureStatusesRpcResult);
3002
+ if ('error' in res) {
3003
+ throw new Error('failed to get signature status: ' + res.error.message);
3004
+ }
3005
+ return res.result;
3006
+ }
3007
+
3008
+ /**
3009
+ * Fetch the current transaction count of the cluster
3010
+ */
3011
+ async getTransactionCount(commitment?: Commitment): Promise<number> {
3012
+ const args = this._buildArgs([], commitment);
3013
+ const unsafeRes = await this._rpcRequest('getTransactionCount', args);
3014
+ const res = create(unsafeRes, jsonRpcResult(number()));
3015
+ if ('error' in res) {
3016
+ throw new Error('failed to get transaction count: ' + res.error.message);
3017
+ }
3018
+ return res.result;
3019
+ }
3020
+
3021
+ /**
3022
+ * Fetch the current total currency supply of the cluster in lamports
3023
+ *
3024
+ * @deprecated Deprecated since v1.2.8. Please use {@link getSupply} instead.
3025
+ */
3026
+ async getTotalSupply(commitment?: Commitment): Promise<number> {
3027
+ const result = await this.getSupply({
3028
+ commitment,
3029
+ excludeNonCirculatingAccountsList: true,
3030
+ });
3031
+ return result.value.total;
3032
+ }
3033
+
3034
+ /**
3035
+ * Fetch the cluster InflationGovernor parameters
3036
+ */
3037
+ async getInflationGovernor(
3038
+ commitment?: Commitment,
3039
+ ): Promise<InflationGovernor> {
3040
+ const args = this._buildArgs([], commitment);
3041
+ const unsafeRes = await this._rpcRequest('getInflationGovernor', args);
3042
+ const res = create(unsafeRes, GetInflationGovernorRpcResult);
3043
+ if ('error' in res) {
3044
+ throw new Error('failed to get inflation: ' + res.error.message);
3045
+ }
3046
+ return res.result;
3047
+ }
3048
+
3049
+ /**
3050
+ * Fetch the inflation reward for a list of addresses for an epoch
3051
+ */
3052
+ async getInflationReward(
3053
+ addresses: PublicKey[],
3054
+ epoch?: number,
3055
+ commitment?: Commitment,
3056
+ ): Promise<(InflationReward | null)[]> {
3057
+ const args = this._buildArgs(
3058
+ [addresses.map(pubkey => pubkey.toBase58())],
3059
+ commitment,
3060
+ undefined,
3061
+ {
3062
+ epoch,
3063
+ },
3064
+ );
3065
+ const unsafeRes = await this._rpcRequest('getInflationReward', args);
3066
+ const res = create(unsafeRes, GetInflationRewardResult);
3067
+ if ('error' in res) {
3068
+ throw new Error('failed to get inflation reward: ' + res.error.message);
3069
+ }
3070
+ return res.result;
3071
+ }
3072
+
3073
+ /**
3074
+ * Fetch the Epoch Info parameters
3075
+ */
3076
+ async getEpochInfo(commitment?: Commitment): Promise<EpochInfo> {
3077
+ const args = this._buildArgs([], commitment);
3078
+ const unsafeRes = await this._rpcRequest('getEpochInfo', args);
3079
+ const res = create(unsafeRes, GetEpochInfoRpcResult);
3080
+ if ('error' in res) {
3081
+ throw new Error('failed to get epoch info: ' + res.error.message);
3082
+ }
3083
+ return res.result;
3084
+ }
3085
+
3086
+ /**
3087
+ * Fetch the Epoch Schedule parameters
3088
+ */
3089
+ async getEpochSchedule(): Promise<EpochSchedule> {
3090
+ const unsafeRes = await this._rpcRequest('getEpochSchedule', []);
3091
+ const res = create(unsafeRes, GetEpochScheduleRpcResult);
3092
+ if ('error' in res) {
3093
+ throw new Error('failed to get epoch schedule: ' + res.error.message);
3094
+ }
3095
+ const epochSchedule = res.result;
3096
+ return new EpochSchedule(
3097
+ epochSchedule.slotsPerEpoch,
3098
+ epochSchedule.leaderScheduleSlotOffset,
3099
+ epochSchedule.warmup,
3100
+ epochSchedule.firstNormalEpoch,
3101
+ epochSchedule.firstNormalSlot,
3102
+ );
3103
+ }
3104
+
3105
+ /**
3106
+ * Fetch the leader schedule for the current epoch
3107
+ * @return {Promise<RpcResponseAndContext<LeaderSchedule>>}
3108
+ */
3109
+ async getLeaderSchedule(): Promise<LeaderSchedule> {
3110
+ const unsafeRes = await this._rpcRequest('getLeaderSchedule', []);
3111
+ const res = create(unsafeRes, GetLeaderScheduleRpcResult);
3112
+ if ('error' in res) {
3113
+ throw new Error('failed to get leader schedule: ' + res.error.message);
3114
+ }
3115
+ return res.result;
3116
+ }
3117
+
3118
+ /**
3119
+ * Fetch the minimum balance needed to exempt an account of `dataLength`
3120
+ * size from rent
3121
+ */
3122
+ async getMinimumBalanceForRentExemption(
3123
+ dataLength: number,
3124
+ commitment?: Commitment,
3125
+ ): Promise<number> {
3126
+ const args = this._buildArgs([dataLength], commitment);
3127
+ const unsafeRes = await this._rpcRequest(
3128
+ 'getMinimumBalanceForRentExemption',
3129
+ args,
3130
+ );
3131
+ const res = create(unsafeRes, GetMinimumBalanceForRentExemptionRpcResult);
3132
+ if ('error' in res) {
3133
+ console.warn('Unable to fetch minimum balance for rent exemption');
3134
+ return 0;
3135
+ }
3136
+ return res.result;
3137
+ }
3138
+
3139
+ /**
3140
+ * Fetch a recent blockhash from the cluster, return with context
3141
+ * @return {Promise<RpcResponseAndContext<{blockhash: Blockhash, feeCalculator: FeeCalculator}>>}
3142
+ *
3143
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link getLatestBlockhash} instead.
3144
+ */
3145
+ async getRecentBlockhashAndContext(
3146
+ commitment?: Commitment,
3147
+ ): Promise<
3148
+ RpcResponseAndContext<{blockhash: Blockhash; feeCalculator: FeeCalculator}>
3149
+ > {
3150
+ const args = this._buildArgs([], commitment);
3151
+ const unsafeRes = await this._rpcRequest('getRecentBlockhash', args);
3152
+ const res = create(unsafeRes, GetRecentBlockhashAndContextRpcResult);
3153
+ if ('error' in res) {
3154
+ throw new Error('failed to get recent blockhash: ' + res.error.message);
3155
+ }
3156
+ return res.result;
3157
+ }
3158
+
3159
+ /**
3160
+ * Fetch recent performance samples
3161
+ * @return {Promise<Array<PerfSample>>}
3162
+ */
3163
+ async getRecentPerformanceSamples(
3164
+ limit?: number,
3165
+ ): Promise<Array<PerfSample>> {
3166
+ const args = this._buildArgs(limit ? [limit] : []);
3167
+ const unsafeRes = await this._rpcRequest(
3168
+ 'getRecentPerformanceSamples',
3169
+ args,
3170
+ );
3171
+ const res = create(unsafeRes, GetRecentPerformanceSamplesRpcResult);
3172
+ if ('error' in res) {
3173
+ throw new Error(
3174
+ 'failed to get recent performance samples: ' + res.error.message,
3175
+ );
3176
+ }
3177
+
3178
+ return res.result;
3179
+ }
3180
+
3181
+ /**
3182
+ * Fetch the fee calculator for a recent blockhash from the cluster, return with context
3183
+ *
3184
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link getFeeForMessage} instead.
3185
+ */
3186
+ async getFeeCalculatorForBlockhash(
3187
+ blockhash: Blockhash,
3188
+ commitment?: Commitment,
3189
+ ): Promise<RpcResponseAndContext<FeeCalculator | null>> {
3190
+ const args = this._buildArgs([blockhash], commitment);
3191
+ const unsafeRes = await this._rpcRequest(
3192
+ 'getFeeCalculatorForBlockhash',
3193
+ args,
3194
+ );
3195
+
3196
+ const res = create(unsafeRes, GetFeeCalculatorRpcResult);
3197
+ if ('error' in res) {
3198
+ throw new Error('failed to get fee calculator: ' + res.error.message);
3199
+ }
3200
+ const {context, value} = res.result;
3201
+ return {
3202
+ context,
3203
+ value: value !== null ? value.feeCalculator : null,
3204
+ };
3205
+ }
3206
+
3207
+ /**
3208
+ * Fetch the fee for a message from the cluster, return with context
3209
+ */
3210
+ async getFeeForMessage(
3211
+ message: Message,
3212
+ commitment?: Commitment,
3213
+ ): Promise<RpcResponseAndContext<number>> {
3214
+ const wireMessage = message.serialize().toString('base64');
3215
+ const args = this._buildArgs([wireMessage], commitment);
3216
+ const unsafeRes = await this._rpcRequest('getFeeForMessage', args);
3217
+
3218
+ const res = create(unsafeRes, jsonRpcResultAndContext(nullable(number())));
3219
+ if ('error' in res) {
3220
+ throw new Error('failed to get slot: ' + res.error.message);
3221
+ }
3222
+ if (res.result === null) {
3223
+ throw new Error('invalid blockhash');
3224
+ }
3225
+ return res.result as unknown as RpcResponseAndContext<number>;
3226
+ }
3227
+
3228
+ /**
3229
+ * Fetch a recent blockhash from the cluster
3230
+ * @return {Promise<{blockhash: Blockhash, feeCalculator: FeeCalculator}>}
3231
+ *
3232
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link getLatestBlockhash} instead.
3233
+ */
3234
+ async getRecentBlockhash(
3235
+ commitment?: Commitment,
3236
+ ): Promise<{blockhash: Blockhash; feeCalculator: FeeCalculator}> {
3237
+ try {
3238
+ const res = await this.getRecentBlockhashAndContext(commitment);
3239
+ return res.value;
3240
+ } catch (e) {
3241
+ throw new Error('failed to get recent blockhash: ' + e);
3242
+ }
3243
+ }
3244
+
3245
+ /**
3246
+ * Fetch the latest blockhash from the cluster
3247
+ * @return {Promise<{blockhash: Blockhash, lastValidBlockHeight: number}>}
3248
+ */
3249
+ async getLatestBlockhash(
3250
+ commitment?: Commitment,
3251
+ ): Promise<{blockhash: Blockhash; lastValidBlockHeight: number}> {
3252
+ try {
3253
+ const res = await this.getLatestBlockhashAndContext(commitment);
3254
+ return res.value;
3255
+ } catch (e) {
3256
+ throw new Error('failed to get recent blockhash: ' + e);
3257
+ }
3258
+ }
3259
+
3260
+ /**
3261
+ * Fetch the latest blockhash from the cluster
3262
+ * @return {Promise<{blockhash: Blockhash, lastValidBlockHeight: number}>}
3263
+ */
3264
+ async getLatestBlockhashAndContext(
3265
+ commitment?: Commitment,
3266
+ ): Promise<
3267
+ RpcResponseAndContext<{blockhash: Blockhash; lastValidBlockHeight: number}>
3268
+ > {
3269
+ const args = this._buildArgs([], commitment);
3270
+ const unsafeRes = await this._rpcRequest('getLatestBlockhash', args);
3271
+ const res = create(unsafeRes, GetLatestBlockhashRpcResult);
3272
+ if ('error' in res) {
3273
+ throw new Error('failed to get latest blockhash: ' + res.error.message);
3274
+ }
3275
+ return res.result;
3276
+ }
3277
+
3278
+ /**
3279
+ * Fetch the node version
3280
+ */
3281
+ async getVersion(): Promise<Version> {
3282
+ const unsafeRes = await this._rpcRequest('getVersion', []);
3283
+ const res = create(unsafeRes, jsonRpcResult(VersionResult));
3284
+ if ('error' in res) {
3285
+ throw new Error('failed to get version: ' + res.error.message);
3286
+ }
3287
+ return res.result;
3288
+ }
3289
+
3290
+ /**
3291
+ * Fetch the genesis hash
3292
+ */
3293
+ async getGenesisHash(): Promise<string> {
3294
+ const unsafeRes = await this._rpcRequest('getGenesisHash', []);
3295
+ const res = create(unsafeRes, jsonRpcResult(string()));
3296
+ if ('error' in res) {
3297
+ throw new Error('failed to get genesis hash: ' + res.error.message);
3298
+ }
3299
+ return res.result;
3300
+ }
3301
+
3302
+ /**
3303
+ * Fetch a processed block from the cluster.
3304
+ */
3305
+ async getBlock(
3306
+ slot: number,
3307
+ opts?: {commitment?: Finality},
3308
+ ): Promise<BlockResponse | null> {
3309
+ const args = this._buildArgsAtLeastConfirmed(
3310
+ [slot],
3311
+ opts && opts.commitment,
3312
+ );
3313
+ const unsafeRes = await this._rpcRequest('getBlock', args);
3314
+ const res = create(unsafeRes, GetBlockRpcResult);
3315
+
3316
+ if ('error' in res) {
3317
+ throw new Error('failed to get confirmed block: ' + res.error.message);
3318
+ }
3319
+
3320
+ const result = res.result;
3321
+ if (!result) return result;
3322
+
3323
+ return {
3324
+ ...result,
3325
+ transactions: result.transactions.map(({transaction, meta}) => {
3326
+ const message = new Message(transaction.message);
3327
+ return {
3328
+ meta,
3329
+ transaction: {
3330
+ ...transaction,
3331
+ message,
3332
+ },
3333
+ };
3334
+ }),
3335
+ };
3336
+ }
3337
+
3338
+ /*
3339
+ * Returns the current block height of the node
3340
+ */
3341
+ async getBlockHeight(commitment?: Commitment): Promise<number> {
3342
+ const args = this._buildArgs([], commitment);
3343
+ const unsafeRes = await this._rpcRequest('getBlockHeight', args);
3344
+ const res = create(unsafeRes, jsonRpcResult(number()));
3345
+ if ('error' in res) {
3346
+ throw new Error(
3347
+ 'failed to get block height information: ' + res.error.message,
3348
+ );
3349
+ }
3350
+
3351
+ return res.result;
3352
+ }
3353
+
3354
+ /*
3355
+ * Returns recent block production information from the current or previous epoch
3356
+ */
3357
+ async getBlockProduction(
3358
+ configOrCommitment?: GetBlockProductionConfig | Commitment,
3359
+ ): Promise<RpcResponseAndContext<BlockProduction>> {
3360
+ let extra: Omit<GetBlockProductionConfig, 'commitment'> | undefined;
3361
+ let commitment: Commitment | undefined;
3362
+
3363
+ if (typeof configOrCommitment === 'string') {
3364
+ commitment = configOrCommitment;
3365
+ } else if (configOrCommitment) {
3366
+ const {commitment: c, ...rest} = configOrCommitment;
3367
+ commitment = c;
3368
+ extra = rest;
3369
+ }
3370
+
3371
+ const args = this._buildArgs([], commitment, 'base64', extra);
3372
+ const unsafeRes = await this._rpcRequest('getBlockProduction', args);
3373
+ const res = create(unsafeRes, BlockProductionResponseStruct);
3374
+ if ('error' in res) {
3375
+ throw new Error(
3376
+ 'failed to get block production information: ' + res.error.message,
3377
+ );
3378
+ }
3379
+
3380
+ return res.result;
3381
+ }
3382
+
3383
+ /**
3384
+ * Fetch a confirmed or finalized transaction from the cluster.
3385
+ */
3386
+ async getTransaction(
3387
+ signature: string,
3388
+ opts?: {commitment?: Finality},
3389
+ ): Promise<TransactionResponse | null> {
3390
+ const args = this._buildArgsAtLeastConfirmed(
3391
+ [signature],
3392
+ opts && opts.commitment,
3393
+ );
3394
+ const unsafeRes = await this._rpcRequest('getTransaction', args);
3395
+ const res = create(unsafeRes, GetTransactionRpcResult);
3396
+ if ('error' in res) {
3397
+ throw new Error('failed to get transaction: ' + res.error.message);
3398
+ }
3399
+
3400
+ const result = res.result;
3401
+ if (!result) return result;
3402
+
3403
+ return {
3404
+ ...result,
3405
+ transaction: {
3406
+ ...result.transaction,
3407
+ message: new Message(result.transaction.message),
3408
+ },
3409
+ };
3410
+ }
3411
+
3412
+ /**
3413
+ * Fetch parsed transaction details for a confirmed or finalized transaction
3414
+ */
3415
+ async getParsedTransaction(
3416
+ signature: TransactionSignature,
3417
+ commitment?: Finality,
3418
+ ): Promise<ParsedConfirmedTransaction | null> {
3419
+ const args = this._buildArgsAtLeastConfirmed(
3420
+ [signature],
3421
+ commitment,
3422
+ 'jsonParsed',
3423
+ );
3424
+ const unsafeRes = await this._rpcRequest('getTransaction', args);
3425
+ const res = create(unsafeRes, GetParsedTransactionRpcResult);
3426
+ if ('error' in res) {
3427
+ throw new Error('failed to get transaction: ' + res.error.message);
3428
+ }
3429
+ return res.result;
3430
+ }
3431
+
3432
+ /**
3433
+ * Fetch parsed transaction details for a batch of confirmed transactions
3434
+ */
3435
+ async getParsedTransactions(
3436
+ signatures: TransactionSignature[],
3437
+ commitment?: Finality,
3438
+ ): Promise<(ParsedConfirmedTransaction | null)[]> {
3439
+ const batch = signatures.map(signature => {
3440
+ const args = this._buildArgsAtLeastConfirmed(
3441
+ [signature],
3442
+ commitment,
3443
+ 'jsonParsed',
3444
+ );
3445
+ return {
3446
+ methodName: 'getTransaction',
3447
+ args,
3448
+ };
3449
+ });
3450
+
3451
+ const unsafeRes = await this._rpcBatchRequest(batch);
3452
+ const res = unsafeRes.map((unsafeRes: any) => {
3453
+ const res = create(unsafeRes, GetParsedTransactionRpcResult);
3454
+ if ('error' in res) {
3455
+ throw new Error('failed to get transactions: ' + res.error.message);
3456
+ }
3457
+ return res.result;
3458
+ });
3459
+
3460
+ return res;
3461
+ }
3462
+
3463
+ /**
3464
+ * Fetch a list of Transactions and transaction statuses from the cluster
3465
+ * for a confirmed block.
3466
+ *
3467
+ * @deprecated Deprecated since v1.13.0. Please use {@link getBlock} instead.
3468
+ */
3469
+ async getConfirmedBlock(
3470
+ slot: number,
3471
+ commitment?: Finality,
3472
+ ): Promise<ConfirmedBlock> {
3473
+ const args = this._buildArgsAtLeastConfirmed([slot], commitment);
3474
+ const unsafeRes = await this._rpcRequest('getConfirmedBlock', args);
3475
+ const res = create(unsafeRes, GetConfirmedBlockRpcResult);
3476
+
3477
+ if ('error' in res) {
3478
+ throw new Error('failed to get confirmed block: ' + res.error.message);
3479
+ }
3480
+
3481
+ const result = res.result;
3482
+ if (!result) {
3483
+ throw new Error('Confirmed block ' + slot + ' not found');
3484
+ }
3485
+
3486
+ const block = {
3487
+ ...result,
3488
+ transactions: result.transactions.map(({transaction, meta}) => {
3489
+ const message = new Message(transaction.message);
3490
+ return {
3491
+ meta,
3492
+ transaction: {
3493
+ ...transaction,
3494
+ message,
3495
+ },
3496
+ };
3497
+ }),
3498
+ };
3499
+
3500
+ return {
3501
+ ...block,
3502
+ transactions: block.transactions.map(({transaction, meta}) => {
3503
+ return {
3504
+ meta,
3505
+ transaction: Transaction.populate(
3506
+ transaction.message,
3507
+ transaction.signatures,
3508
+ ),
3509
+ };
3510
+ }),
3511
+ };
3512
+ }
3513
+
3514
+ /**
3515
+ * Fetch confirmed blocks between two slots
3516
+ */
3517
+ async getBlocks(
3518
+ startSlot: number,
3519
+ endSlot?: number,
3520
+ commitment?: Finality,
3521
+ ): Promise<Array<number>> {
3522
+ const args = this._buildArgsAtLeastConfirmed(
3523
+ endSlot !== undefined ? [startSlot, endSlot] : [startSlot],
3524
+ commitment,
3525
+ );
3526
+ const unsafeRes = await this._rpcRequest('getBlocks', args);
3527
+ const res = create(unsafeRes, jsonRpcResult(array(number())));
3528
+ if ('error' in res) {
3529
+ throw new Error('failed to get blocks: ' + res.error.message);
3530
+ }
3531
+ return res.result;
3532
+ }
3533
+
3534
+ /**
3535
+ * Fetch a list of Signatures from the cluster for a block, excluding rewards
3536
+ */
3537
+ async getBlockSignatures(
3538
+ slot: number,
3539
+ commitment?: Finality,
3540
+ ): Promise<BlockSignatures> {
3541
+ const args = this._buildArgsAtLeastConfirmed(
3542
+ [slot],
3543
+ commitment,
3544
+ undefined,
3545
+ {
3546
+ transactionDetails: 'signatures',
3547
+ rewards: false,
3548
+ },
3549
+ );
3550
+ const unsafeRes = await this._rpcRequest('getBlock', args);
3551
+ const res = create(unsafeRes, GetBlockSignaturesRpcResult);
3552
+ if ('error' in res) {
3553
+ throw new Error('failed to get block: ' + res.error.message);
3554
+ }
3555
+ const result = res.result;
3556
+ if (!result) {
3557
+ throw new Error('Block ' + slot + ' not found');
3558
+ }
3559
+ return result;
3560
+ }
3561
+
3562
+ /**
3563
+ * Fetch a list of Signatures from the cluster for a confirmed block, excluding rewards
3564
+ *
3565
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link getBlockSignatures} instead.
3566
+ */
3567
+ async getConfirmedBlockSignatures(
3568
+ slot: number,
3569
+ commitment?: Finality,
3570
+ ): Promise<BlockSignatures> {
3571
+ const args = this._buildArgsAtLeastConfirmed(
3572
+ [slot],
3573
+ commitment,
3574
+ undefined,
3575
+ {
3576
+ transactionDetails: 'signatures',
3577
+ rewards: false,
3578
+ },
3579
+ );
3580
+ const unsafeRes = await this._rpcRequest('getConfirmedBlock', args);
3581
+ const res = create(unsafeRes, GetBlockSignaturesRpcResult);
3582
+ if ('error' in res) {
3583
+ throw new Error('failed to get confirmed block: ' + res.error.message);
3584
+ }
3585
+ const result = res.result;
3586
+ if (!result) {
3587
+ throw new Error('Confirmed block ' + slot + ' not found');
3588
+ }
3589
+ return result;
3590
+ }
3591
+
3592
+ /**
3593
+ * Fetch a transaction details for a confirmed transaction
3594
+ *
3595
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link getTransaction} instead.
3596
+ */
3597
+ async getConfirmedTransaction(
3598
+ signature: TransactionSignature,
3599
+ commitment?: Finality,
3600
+ ): Promise<ConfirmedTransaction | null> {
3601
+ const args = this._buildArgsAtLeastConfirmed([signature], commitment);
3602
+ const unsafeRes = await this._rpcRequest('getConfirmedTransaction', args);
3603
+ const res = create(unsafeRes, GetTransactionRpcResult);
3604
+ if ('error' in res) {
3605
+ throw new Error('failed to get transaction: ' + res.error.message);
3606
+ }
3607
+
3608
+ const result = res.result;
3609
+ if (!result) return result;
3610
+
3611
+ const message = new Message(result.transaction.message);
3612
+ const signatures = result.transaction.signatures;
3613
+ return {
3614
+ ...result,
3615
+ transaction: Transaction.populate(message, signatures),
3616
+ };
3617
+ }
3618
+
3619
+ /**
3620
+ * Fetch parsed transaction details for a confirmed transaction
3621
+ *
3622
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link getParsedTransaction} instead.
3623
+ */
3624
+ async getParsedConfirmedTransaction(
3625
+ signature: TransactionSignature,
3626
+ commitment?: Finality,
3627
+ ): Promise<ParsedConfirmedTransaction | null> {
3628
+ const args = this._buildArgsAtLeastConfirmed(
3629
+ [signature],
3630
+ commitment,
3631
+ 'jsonParsed',
3632
+ );
3633
+ const unsafeRes = await this._rpcRequest('getConfirmedTransaction', args);
3634
+ const res = create(unsafeRes, GetParsedTransactionRpcResult);
3635
+ if ('error' in res) {
3636
+ throw new Error(
3637
+ 'failed to get confirmed transaction: ' + res.error.message,
3638
+ );
3639
+ }
3640
+ return res.result;
3641
+ }
3642
+
3643
+ /**
3644
+ * Fetch parsed transaction details for a batch of confirmed transactions
3645
+ *
3646
+ * @deprecated Deprecated since Solana v1.8.0. Please use {@link getParsedTransactions} instead.
3647
+ */
3648
+ async getParsedConfirmedTransactions(
3649
+ signatures: TransactionSignature[],
3650
+ commitment?: Finality,
3651
+ ): Promise<(ParsedConfirmedTransaction | null)[]> {
3652
+ const batch = signatures.map(signature => {
3653
+ const args = this._buildArgsAtLeastConfirmed(
3654
+ [signature],
3655
+ commitment,
3656
+ 'jsonParsed',
3657
+ );
3658
+ return {
3659
+ methodName: 'getConfirmedTransaction',
3660
+ args,
3661
+ };
3662
+ });
3663
+
3664
+ const unsafeRes = await this._rpcBatchRequest(batch);
3665
+ const res = unsafeRes.map((unsafeRes: any) => {
3666
+ const res = create(unsafeRes, GetParsedTransactionRpcResult);
3667
+ if ('error' in res) {
3668
+ throw new Error(
3669
+ 'failed to get confirmed transactions: ' + res.error.message,
3670
+ );
3671
+ }
3672
+ return res.result;
3673
+ });
3674
+
3675
+ return res;
3676
+ }
3677
+
3678
+ /**
3679
+ * Fetch a list of all the confirmed signatures for transactions involving an address
3680
+ * within a specified slot range. Max range allowed is 10,000 slots.
3681
+ *
3682
+ * @deprecated Deprecated since v1.3. Please use {@link getConfirmedSignaturesForAddress2} instead.
3683
+ *
3684
+ * @param address queried address
3685
+ * @param startSlot start slot, inclusive
3686
+ * @param endSlot end slot, inclusive
3687
+ */
3688
+ async getConfirmedSignaturesForAddress(
3689
+ address: PublicKey,
3690
+ startSlot: number,
3691
+ endSlot: number,
3692
+ ): Promise<Array<TransactionSignature>> {
3693
+ let options: any = {};
3694
+
3695
+ let firstAvailableBlock = await this.getFirstAvailableBlock();
3696
+ while (!('until' in options)) {
3697
+ startSlot--;
3698
+ if (startSlot <= 0 || startSlot < firstAvailableBlock) {
3699
+ break;
3700
+ }
3701
+
3702
+ try {
3703
+ const block = await this.getConfirmedBlockSignatures(
3704
+ startSlot,
3705
+ 'finalized',
3706
+ );
3707
+ if (block.signatures.length > 0) {
3708
+ options.until =
3709
+ block.signatures[block.signatures.length - 1].toString();
3710
+ }
3711
+ } catch (err) {
3712
+ if (err instanceof Error && err.message.includes('skipped')) {
3713
+ continue;
3714
+ } else {
3715
+ throw err;
3716
+ }
3717
+ }
3718
+ }
3719
+
3720
+ let highestConfirmedRoot = await this.getSlot('finalized');
3721
+ while (!('before' in options)) {
3722
+ endSlot++;
3723
+ if (endSlot > highestConfirmedRoot) {
3724
+ break;
3725
+ }
3726
+
3727
+ try {
3728
+ const block = await this.getConfirmedBlockSignatures(endSlot);
3729
+ if (block.signatures.length > 0) {
3730
+ options.before =
3731
+ block.signatures[block.signatures.length - 1].toString();
3732
+ }
3733
+ } catch (err) {
3734
+ if (err instanceof Error && err.message.includes('skipped')) {
3735
+ continue;
3736
+ } else {
3737
+ throw err;
3738
+ }
3739
+ }
3740
+ }
3741
+
3742
+ const confirmedSignatureInfo = await this.getConfirmedSignaturesForAddress2(
3743
+ address,
3744
+ options,
3745
+ );
3746
+ return confirmedSignatureInfo.map(info => info.signature);
3747
+ }
3748
+
3749
+ /**
3750
+ * Returns confirmed signatures for transactions involving an
3751
+ * address backwards in time from the provided signature or most recent confirmed block
3752
+ *
3753
+ *
3754
+ * @param address queried address
3755
+ * @param options
3756
+ */
3757
+ async getConfirmedSignaturesForAddress2(
3758
+ address: PublicKey,
3759
+ options?: ConfirmedSignaturesForAddress2Options,
3760
+ commitment?: Finality,
3761
+ ): Promise<Array<ConfirmedSignatureInfo>> {
3762
+ const args = this._buildArgsAtLeastConfirmed(
3763
+ [address.toBase58()],
3764
+ commitment,
3765
+ undefined,
3766
+ options,
3767
+ );
3768
+ const unsafeRes = await this._rpcRequest(
3769
+ 'getConfirmedSignaturesForAddress2',
3770
+ args,
3771
+ );
3772
+ const res = create(unsafeRes, GetConfirmedSignaturesForAddress2RpcResult);
3773
+ if ('error' in res) {
3774
+ throw new Error(
3775
+ 'failed to get confirmed signatures for address: ' + res.error.message,
3776
+ );
3777
+ }
3778
+ return res.result;
3779
+ }
3780
+
3781
+ /**
3782
+ * Returns confirmed signatures for transactions involving an
3783
+ * address backwards in time from the provided signature or most recent confirmed block
3784
+ *
3785
+ *
3786
+ * @param address queried address
3787
+ * @param options
3788
+ */
3789
+ async getSignaturesForAddress(
3790
+ address: PublicKey,
3791
+ options?: SignaturesForAddressOptions,
3792
+ commitment?: Finality,
3793
+ ): Promise<Array<ConfirmedSignatureInfo>> {
3794
+ const args = this._buildArgsAtLeastConfirmed(
3795
+ [address.toBase58()],
3796
+ commitment,
3797
+ undefined,
3798
+ options,
3799
+ );
3800
+ const unsafeRes = await this._rpcRequest('getSignaturesForAddress', args);
3801
+ const res = create(unsafeRes, GetSignaturesForAddressRpcResult);
3802
+ if ('error' in res) {
3803
+ throw new Error(
3804
+ 'failed to get signatures for address: ' + res.error.message,
3805
+ );
3806
+ }
3807
+ return res.result;
3808
+ }
3809
+
3810
+ /**
3811
+ * Fetch the contents of a Nonce account from the cluster, return with context
3812
+ */
3813
+ async getNonceAndContext(
3814
+ nonceAccount: PublicKey,
3815
+ commitment?: Commitment,
3816
+ ): Promise<RpcResponseAndContext<NonceAccount | null>> {
3817
+ const {context, value: accountInfo} = await this.getAccountInfoAndContext(
3818
+ nonceAccount,
3819
+ commitment,
3820
+ );
3821
+
3822
+ let value = null;
3823
+ if (accountInfo !== null) {
3824
+ value = NonceAccount.fromAccountData(accountInfo.data);
3825
+ }
3826
+
3827
+ return {
3828
+ context,
3829
+ value,
3830
+ };
3831
+ }
3832
+
3833
+ /**
3834
+ * Fetch the contents of a Nonce account from the cluster
3835
+ */
3836
+ async getNonce(
3837
+ nonceAccount: PublicKey,
3838
+ commitment?: Commitment,
3839
+ ): Promise<NonceAccount | null> {
3840
+ return await this.getNonceAndContext(nonceAccount, commitment)
3841
+ .then(x => x.value)
3842
+ .catch(e => {
3843
+ throw new Error(
3844
+ 'failed to get nonce for account ' +
3845
+ nonceAccount.toBase58() +
3846
+ ': ' +
3847
+ e,
3848
+ );
3849
+ });
3850
+ }
3851
+
3852
+ /**
3853
+ * Request an allocation of lamports to the specified address
3854
+ *
3855
+ * ```typescript
3856
+ * import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
3857
+ *
3858
+ * (async () => {
3859
+ * const connection = new Connection("https://api.testnet.solana.com", "confirmed");
3860
+ * const myAddress = new PublicKey("2nr1bHFT86W9tGnyvmYW4vcHKsQB3sVQfnddasz4kExM");
3861
+ * const signature = await connection.requestAirdrop(myAddress, LAMPORTS_PER_SOL);
3862
+ * await connection.confirmTransaction(signature);
3863
+ * })();
3864
+ * ```
3865
+ */
3866
+ async requestAirdrop(
3867
+ to: PublicKey,
3868
+ lamports: number,
3869
+ ): Promise<TransactionSignature> {
3870
+ const unsafeRes = await this._rpcRequest('requestAirdrop', [
3871
+ to.toBase58(),
3872
+ lamports,
3873
+ ]);
3874
+ const res = create(unsafeRes, RequestAirdropRpcResult);
3875
+ if ('error' in res) {
3876
+ throw new Error(
3877
+ 'airdrop to ' + to.toBase58() + ' failed: ' + res.error.message,
3878
+ );
3879
+ }
3880
+ return res.result;
3881
+ }
3882
+
3883
+ /**
3884
+ * @internal
3885
+ */
3886
+ async _recentBlockhash(disableCache: boolean): Promise<Blockhash> {
3887
+ if (!disableCache) {
3888
+ // Wait for polling to finish
3889
+ while (this._pollingBlockhash) {
3890
+ await sleep(100);
3891
+ }
3892
+ const timeSinceFetch = Date.now() - this._blockhashInfo.lastFetch;
3893
+ const expired = timeSinceFetch >= BLOCKHASH_CACHE_TIMEOUT_MS;
3894
+ if (this._blockhashInfo.recentBlockhash !== null && !expired) {
3895
+ return this._blockhashInfo.recentBlockhash;
3896
+ }
3897
+ }
3898
+
3899
+ return await this._pollNewBlockhash();
3900
+ }
3901
+
3902
+ /**
3903
+ * @internal
3904
+ */
3905
+ async _pollNewBlockhash(): Promise<Blockhash> {
3906
+ this._pollingBlockhash = true;
3907
+ try {
3908
+ const startTime = Date.now();
3909
+ for (let i = 0; i < 50; i++) {
3910
+ const {blockhash} = await this.getRecentBlockhash('finalized');
3911
+
3912
+ if (this._blockhashInfo.recentBlockhash != blockhash) {
3913
+ this._blockhashInfo = {
3914
+ recentBlockhash: blockhash,
3915
+ lastFetch: Date.now(),
3916
+ transactionSignatures: [],
3917
+ simulatedSignatures: [],
3918
+ };
3919
+ return blockhash;
3920
+ }
3921
+
3922
+ // Sleep for approximately half a slot
3923
+ await sleep(MS_PER_SLOT / 2);
3924
+ }
3925
+
3926
+ throw new Error(
3927
+ `Unable to obtain a new blockhash after ${Date.now() - startTime}ms`,
3928
+ );
3929
+ } finally {
3930
+ this._pollingBlockhash = false;
3931
+ }
3932
+ }
3933
+
3934
+ /**
3935
+ * Simulate a transaction
3936
+ */
3937
+ async simulateTransaction(
3938
+ transactionOrMessage: Transaction | Message,
3939
+ signers?: Array<Signer>,
3940
+ includeAccounts?: boolean | Array<PublicKey>,
3941
+ ): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
3942
+ let transaction;
3943
+ if (transactionOrMessage instanceof Transaction) {
3944
+ let originalTx: Transaction = transactionOrMessage;
3945
+ transaction = new Transaction({
3946
+ recentBlockhash: originalTx.recentBlockhash,
3947
+ nonceInfo: originalTx.nonceInfo,
3948
+ feePayer: originalTx.feePayer,
3949
+ signatures: [...originalTx.signatures],
3950
+ });
3951
+ transaction.instructions = transactionOrMessage.instructions;
3952
+ } else {
3953
+ transaction = Transaction.populate(transactionOrMessage);
3954
+ // HACK: this function relies on mutating the populated transaction
3955
+ transaction._message = transaction._json = undefined;
3956
+ }
3957
+
3958
+ if (transaction.nonceInfo && signers) {
3959
+ transaction.sign(...signers);
3960
+ } else {
3961
+ let disableCache = this._disableBlockhashCaching;
3962
+ for (;;) {
3963
+ transaction.recentBlockhash = await this._recentBlockhash(disableCache);
3964
+
3965
+ if (!signers) break;
3966
+
3967
+ transaction.sign(...signers);
3968
+ if (!transaction.signature) {
3969
+ throw new Error('!signature'); // should never happen
3970
+ }
3971
+
3972
+ const signature = transaction.signature.toString('base64');
3973
+ if (
3974
+ !this._blockhashInfo.simulatedSignatures.includes(signature) &&
3975
+ !this._blockhashInfo.transactionSignatures.includes(signature)
3976
+ ) {
3977
+ // The signature of this transaction has not been seen before with the
3978
+ // current recentBlockhash, all done. Let's break
3979
+ this._blockhashInfo.simulatedSignatures.push(signature);
3980
+ break;
3981
+ } else {
3982
+ // This transaction would be treated as duplicate (its derived signature
3983
+ // matched to one of already recorded signatures).
3984
+ // So, we must fetch a new blockhash for a different signature by disabling
3985
+ // our cache not to wait for the cache expiration (BLOCKHASH_CACHE_TIMEOUT_MS).
3986
+ disableCache = true;
3987
+ }
3988
+ }
3989
+ }
3990
+
3991
+ const message = transaction._compile();
3992
+ const signData = message.serialize();
3993
+ const wireTransaction = transaction._serialize(signData);
3994
+ const encodedTransaction = wireTransaction.toString('base64');
3995
+ const config: any = {
3996
+ encoding: 'base64',
3997
+ commitment: this.commitment,
3998
+ };
3999
+
4000
+ if (includeAccounts) {
4001
+ const addresses = (
4002
+ Array.isArray(includeAccounts)
4003
+ ? includeAccounts
4004
+ : message.nonProgramIds()
4005
+ ).map(key => key.toBase58());
4006
+
4007
+ config['accounts'] = {
4008
+ encoding: 'base64',
4009
+ addresses,
4010
+ };
4011
+ }
4012
+
4013
+ if (signers) {
4014
+ config.sigVerify = true;
4015
+ }
4016
+
4017
+ const args = [encodedTransaction, config];
4018
+ const unsafeRes = await this._rpcRequest('simulateTransaction', args);
4019
+ const res = create(unsafeRes, SimulatedTransactionResponseStruct);
4020
+ if ('error' in res) {
4021
+ let logs;
4022
+ if ('data' in res.error) {
4023
+ logs = res.error.data.logs;
4024
+ if (logs && Array.isArray(logs)) {
4025
+ const traceIndent = '\n ';
4026
+ const logTrace = traceIndent + logs.join(traceIndent);
4027
+ console.error(res.error.message, logTrace);
4028
+ }
4029
+ }
4030
+ throw new SendTransactionError(
4031
+ 'failed to simulate transaction: ' + res.error.message,
4032
+ logs,
4033
+ );
4034
+ }
4035
+ return res.result;
4036
+ }
4037
+
4038
+ /**
4039
+ * Sign and send a transaction
4040
+ */
4041
+ async sendTransaction(
4042
+ transaction: Transaction,
4043
+ signers: Array<Signer>,
4044
+ options?: SendOptions,
4045
+ ): Promise<TransactionSignature> {
4046
+ if (transaction.nonceInfo) {
4047
+ transaction.sign(...signers);
4048
+ } else {
4049
+ let disableCache = this._disableBlockhashCaching;
4050
+ for (;;) {
4051
+ transaction.recentBlockhash = await this._recentBlockhash(disableCache);
4052
+ transaction.sign(...signers);
4053
+ if (!transaction.signature) {
4054
+ throw new Error('!signature'); // should never happen
4055
+ }
4056
+
4057
+ const signature = transaction.signature.toString('base64');
4058
+ if (!this._blockhashInfo.transactionSignatures.includes(signature)) {
4059
+ // The signature of this transaction has not been seen before with the
4060
+ // current recentBlockhash, all done. Let's break
4061
+ this._blockhashInfo.transactionSignatures.push(signature);
4062
+ break;
4063
+ } else {
4064
+ // This transaction would be treated as duplicate (its derived signature
4065
+ // matched to one of already recorded signatures).
4066
+ // So, we must fetch a new blockhash for a different signature by disabling
4067
+ // our cache not to wait for the cache expiration (BLOCKHASH_CACHE_TIMEOUT_MS).
4068
+ disableCache = true;
4069
+ }
4070
+ }
4071
+ }
4072
+
4073
+ const wireTransaction = transaction.serialize();
4074
+ return await this.sendRawTransaction(wireTransaction, options);
4075
+ }
4076
+
4077
+ /**
4078
+ * Send a transaction that has already been signed and serialized into the
4079
+ * wire format
4080
+ */
4081
+ async sendRawTransaction(
4082
+ rawTransaction: Buffer | Uint8Array | Array<number>,
4083
+ options?: SendOptions,
4084
+ ): Promise<TransactionSignature> {
4085
+ const encodedTransaction = toBuffer(rawTransaction).toString('base64');
4086
+ const result = await this.sendEncodedTransaction(
4087
+ encodedTransaction,
4088
+ options,
4089
+ );
4090
+ return result;
4091
+ }
4092
+
4093
+ /**
4094
+ * Send a transaction that has already been signed, serialized into the
4095
+ * wire format, and encoded as a base64 string
4096
+ */
4097
+ async sendEncodedTransaction(
4098
+ encodedTransaction: string,
4099
+ options?: SendOptions,
4100
+ ): Promise<TransactionSignature> {
4101
+ const config: any = {encoding: 'base64'};
4102
+ const skipPreflight = options && options.skipPreflight;
4103
+ const preflightCommitment =
4104
+ (options && options.preflightCommitment) || this.commitment;
4105
+
4106
+ if (options && options.maxRetries) {
4107
+ config.maxRetries = options.maxRetries;
4108
+ }
4109
+ if (skipPreflight) {
4110
+ config.skipPreflight = skipPreflight;
4111
+ }
4112
+ if (preflightCommitment) {
4113
+ config.preflightCommitment = preflightCommitment;
4114
+ }
4115
+
4116
+ const args = [encodedTransaction, config];
4117
+ const unsafeRes = await this._rpcRequest('sendTransaction', args);
4118
+ const res = create(unsafeRes, SendTransactionRpcResult);
4119
+ if ('error' in res) {
4120
+ let logs;
4121
+ if ('data' in res.error) {
4122
+ logs = res.error.data.logs;
4123
+ }
4124
+ throw new SendTransactionError(
4125
+ 'failed to send transaction: ' + res.error.message,
4126
+ logs,
4127
+ );
4128
+ }
4129
+ return res.result;
4130
+ }
4131
+
4132
+ /**
4133
+ * @internal
4134
+ */
4135
+ _wsOnOpen() {
4136
+ this._rpcWebSocketConnected = true;
4137
+ this._rpcWebSocketHeartbeat = setInterval(() => {
4138
+ // Ping server every 5s to prevent idle timeouts
4139
+ this._rpcWebSocket.notify('ping').catch(() => {});
4140
+ }, 5000);
4141
+ this._updateSubscriptions();
4142
+ }
4143
+
4144
+ /**
4145
+ * @internal
4146
+ */
4147
+ _wsOnError(err: Error) {
4148
+ console.error('ws error:', err.message);
4149
+ }
4150
+
4151
+ /**
4152
+ * @internal
4153
+ */
4154
+ _wsOnClose(code: number) {
4155
+ if (this._rpcWebSocketHeartbeat) {
4156
+ clearInterval(this._rpcWebSocketHeartbeat);
4157
+ this._rpcWebSocketHeartbeat = null;
4158
+ }
4159
+
4160
+ if (code === 1000) {
4161
+ // explicit close, check if any subscriptions have been made since close
4162
+ this._updateSubscriptions();
4163
+ return;
4164
+ }
4165
+
4166
+ // implicit close, prepare subscriptions for auto-reconnect
4167
+ this._subscriptionCallbacksByServerSubscriptionId = {};
4168
+ Object.entries(
4169
+ this._subscriptionsByHash as Record<SubscriptionConfigHash, Subscription>,
4170
+ ).forEach(([hash, subscription]) => {
4171
+ this._subscriptionsByHash[hash] = {
4172
+ ...subscription,
4173
+ state: 'pending',
4174
+ };
4175
+ });
4176
+ }
4177
+
4178
+ /**
4179
+ * @internal
4180
+ */
4181
+ async _updateSubscriptions() {
4182
+ if (Object.keys(this._subscriptionsByHash).length === 0) {
4183
+ if (this._rpcWebSocketConnected) {
4184
+ this._rpcWebSocketConnected = false;
4185
+ this._rpcWebSocketIdleTimeout = setTimeout(() => {
4186
+ this._rpcWebSocketIdleTimeout = null;
4187
+ try {
4188
+ this._rpcWebSocket.close();
4189
+ } catch (err) {
4190
+ // swallow error if socket has already been closed.
4191
+ if (err instanceof Error) {
4192
+ console.log(
4193
+ `Error when closing socket connection: ${err.message}`,
4194
+ );
4195
+ }
4196
+ }
4197
+ }, 500);
4198
+ }
4199
+ return;
4200
+ }
4201
+
4202
+ if (this._rpcWebSocketIdleTimeout !== null) {
4203
+ clearTimeout(this._rpcWebSocketIdleTimeout);
4204
+ this._rpcWebSocketIdleTimeout = null;
4205
+ this._rpcWebSocketConnected = true;
4206
+ }
4207
+
4208
+ if (!this._rpcWebSocketConnected) {
4209
+ this._rpcWebSocket.connect();
4210
+ return;
4211
+ }
4212
+
4213
+ await Promise.all(
4214
+ Object.entries(
4215
+ this._subscriptionsByHash as Record<
4216
+ SubscriptionConfigHash,
4217
+ Subscription
4218
+ >,
4219
+ ).map(async ([hash, subscription]) => {
4220
+ switch (subscription.state) {
4221
+ case 'pending':
4222
+ case 'unsubscribed':
4223
+ if (subscription.callbacks.size === 0) {
4224
+ /**
4225
+ * You can end up here when:
4226
+ *
4227
+ * - a subscription has recently unsubscribed
4228
+ * without having new callbacks added to it
4229
+ * while the unsubscribe was in flight, or
4230
+ * - when a pending subscription has its
4231
+ * listeners removed before a request was
4232
+ * sent to the server.
4233
+ *
4234
+ * Being that nobody is interested in this
4235
+ * subscription any longer, delete it.
4236
+ */
4237
+ delete this._subscriptionsByHash[hash];
4238
+ if (subscription.state === 'unsubscribed') {
4239
+ delete this._subscriptionCallbacksByServerSubscriptionId[
4240
+ subscription.serverSubscriptionId
4241
+ ];
4242
+ }
4243
+ await this._updateSubscriptions();
4244
+ return;
4245
+ }
4246
+ await (async () => {
4247
+ const {method, params} = subscription;
4248
+ let args: IWSRequestParams;
4249
+ switch (method) {
4250
+ case 'accountSubscribe':
4251
+ args = this._buildArgs(
4252
+ [params.publicKey],
4253
+ params.commitment,
4254
+ 'base64',
4255
+ );
4256
+ break;
4257
+ case 'logsSubscribe':
4258
+ args = this._buildArgs(
4259
+ [
4260
+ typeof params.filter === 'object'
4261
+ ? {mentions: [params.filter.toString()]}
4262
+ : params.filter,
4263
+ ],
4264
+ params.commitment,
4265
+ );
4266
+ break;
4267
+ case 'programSubscribe':
4268
+ args = this._buildArgs(
4269
+ [params.programId],
4270
+ params.commitment,
4271
+ 'base64',
4272
+ params.filters
4273
+ ? {
4274
+ filters: params.filters,
4275
+ }
4276
+ : undefined,
4277
+ );
4278
+ break;
4279
+ case 'signatureSubscribe':
4280
+ args = [params.signature, params.options].filter(Boolean);
4281
+ break;
4282
+ case 'rootSubscribe':
4283
+ case 'slotSubscribe':
4284
+ case 'slotsUpdatesSubscribe':
4285
+ args = [];
4286
+ break;
4287
+ }
4288
+ try {
4289
+ this._subscriptionsByHash[hash] = {
4290
+ ...subscription,
4291
+ state: 'subscribing',
4292
+ };
4293
+ const serverSubscriptionId: ServerSubscriptionId =
4294
+ (await this._rpcWebSocket.call(method, args)) as number;
4295
+ this._subscriptionsByHash[hash] = {
4296
+ ...subscription,
4297
+ serverSubscriptionId,
4298
+ state: 'subscribed',
4299
+ };
4300
+ this._subscriptionCallbacksByServerSubscriptionId[
4301
+ serverSubscriptionId
4302
+ ] = subscription.callbacks;
4303
+ } catch (e) {
4304
+ if (e instanceof Error) {
4305
+ console.error(
4306
+ `${method} error for argument`,
4307
+ args,
4308
+ e.message,
4309
+ );
4310
+ }
4311
+ // TODO: Maybe add an 'errored' state or a retry limit?
4312
+ this._subscriptionsByHash[hash] = {
4313
+ ...subscription,
4314
+ state: 'pending',
4315
+ };
4316
+ } finally {
4317
+ await this._updateSubscriptions();
4318
+ }
4319
+ })();
4320
+ break;
4321
+ case 'subscribed':
4322
+ if (subscription.callbacks.size === 0) {
4323
+ // By the time we successfully set up a subscription
4324
+ // with the server, the client stopped caring about it.
4325
+ // Tear it down now.
4326
+ await (async () => {
4327
+ const {serverSubscriptionId, unsubscribeMethod} = subscription;
4328
+ if (
4329
+ this._subscriptionsAutoDisposedByRpc.has(serverSubscriptionId)
4330
+ ) {
4331
+ /**
4332
+ * Special case.
4333
+ * If we're dealing with a subscription that has been auto-
4334
+ * disposed by the RPC, then we can skip the RPC call to
4335
+ * tear down the subscription here.
4336
+ *
4337
+ * NOTE: There is a proposal to eliminate this special case, here:
4338
+ * https://github.com/solana-labs/solana/issues/18892
4339
+ */
4340
+ this._subscriptionsAutoDisposedByRpc.delete(
4341
+ serverSubscriptionId,
4342
+ );
4343
+ } else {
4344
+ this._subscriptionsByHash[hash] = {
4345
+ ...subscription,
4346
+ state: 'unsubscribing',
4347
+ };
4348
+ try {
4349
+ await this._rpcWebSocket.call(unsubscribeMethod, [
4350
+ serverSubscriptionId,
4351
+ ]);
4352
+ } catch (e) {
4353
+ if (e instanceof Error) {
4354
+ console.error(`${unsubscribeMethod} error:`, e.message);
4355
+ }
4356
+ // TODO: Maybe add an 'errored' state or a retry limit?
4357
+ this._subscriptionsByHash[hash] = {
4358
+ ...subscription,
4359
+ state: 'subscribed',
4360
+ };
4361
+ await this._updateSubscriptions();
4362
+ return;
4363
+ }
4364
+ }
4365
+ this._subscriptionsByHash[hash] = {
4366
+ ...subscription,
4367
+ state: 'unsubscribed',
4368
+ };
4369
+ await this._updateSubscriptions();
4370
+ })();
4371
+ }
4372
+ break;
4373
+ case 'subscribing':
4374
+ case 'unsubscribing':
4375
+ break;
4376
+ }
4377
+ }),
4378
+ );
4379
+ }
4380
+
4381
+ /**
4382
+ *
4383
+ * @internal
4384
+ */
4385
+ _publishNotification<TCallback extends SubscriptionConfig['callback']>(
4386
+ serverSubscriptionId: ServerSubscriptionId,
4387
+ publishArgs: Parameters<TCallback>,
4388
+ ): void {
4389
+ const callbacks =
4390
+ this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId];
4391
+ if (callbacks == null) {
4392
+ return;
4393
+ }
4394
+ callbacks.forEach(cb => {
4395
+ try {
4396
+ cb(...(publishArgs as [any, any]));
4397
+ } catch (e) {
4398
+ console.error(e);
4399
+ }
4400
+ });
4401
+ }
4402
+
4403
+ /**
4404
+ * @internal
4405
+ */
4406
+ _wsOnAccountNotification(notification: object) {
4407
+ const {result, subscription} = create(
4408
+ notification,
4409
+ AccountNotificationResult,
4410
+ );
4411
+ this._publishNotification<AccountChangeCallback>(subscription, [
4412
+ result.value,
4413
+ result.context,
4414
+ ]);
4415
+ }
4416
+
4417
+ /**
4418
+ * @private
4419
+ */
4420
+ _makeSubscription(
4421
+ // The subscription config, before applying defaults
4422
+ rawConfig: SubscriptionConfig,
4423
+ ): ClientSubscriptionId {
4424
+ const clientSubscriptionId = this._nextClientSubscriptionId++;
4425
+ let subscriptionConfig: SubscriptionConfig;
4426
+ // Apply defaults.
4427
+ switch (rawConfig.method) {
4428
+ case 'accountSubscribe':
4429
+ case 'logsSubscribe':
4430
+ case 'programSubscribe':
4431
+ if (rawConfig.params.commitment === undefined) {
4432
+ subscriptionConfig = {
4433
+ ...rawConfig,
4434
+ params: {
4435
+ ...rawConfig.params,
4436
+ commitment: 'finalized',
4437
+ },
4438
+ } as SubscriptionConfig;
4439
+ } else {
4440
+ subscriptionConfig = rawConfig;
4441
+ }
4442
+ break;
4443
+ case 'signatureSubscribe':
4444
+ if (
4445
+ rawConfig.params.options &&
4446
+ rawConfig.params.options.commitment === undefined
4447
+ ) {
4448
+ subscriptionConfig = {
4449
+ ...rawConfig,
4450
+ params: {
4451
+ ...rawConfig.params,
4452
+ options: {
4453
+ ...rawConfig.params.options,
4454
+ commitment: 'finalized',
4455
+ },
4456
+ },
4457
+ };
4458
+ } else {
4459
+ subscriptionConfig = rawConfig;
4460
+ }
4461
+ break;
4462
+ default:
4463
+ subscriptionConfig = rawConfig;
4464
+ }
4465
+ const hash = hashSubscriptionConfig(subscriptionConfig);
4466
+ const existingSubscription = this._subscriptionsByHash[hash];
4467
+ if (existingSubscription == null) {
4468
+ this._subscriptionsByHash[hash] = {
4469
+ ...subscriptionConfig,
4470
+ callbacks: new Set([subscriptionConfig.callback]),
4471
+ state: 'pending',
4472
+ };
4473
+ } else {
4474
+ existingSubscription.callbacks.add(subscriptionConfig.callback);
4475
+ }
4476
+ let unsubscribed = false;
4477
+ this._subscriptionDisposeFunctionsByClientSubscriptionId[
4478
+ clientSubscriptionId
4479
+ ] = async () => {
4480
+ if (unsubscribed) {
4481
+ return;
4482
+ }
4483
+ unsubscribed = true;
4484
+ delete this._subscriptionDisposeFunctionsByClientSubscriptionId[
4485
+ clientSubscriptionId
4486
+ ];
4487
+ const subscription = this._subscriptionsByHash[hash];
4488
+ assert(
4489
+ subscription,
4490
+ `Could not find a \`Subscription\` when tearing down client subscription #${clientSubscriptionId}`,
4491
+ );
4492
+ subscription.callbacks.delete(subscriptionConfig.callback);
4493
+ await this._updateSubscriptions();
4494
+ };
4495
+ this._updateSubscriptions();
4496
+ return clientSubscriptionId;
4497
+ }
4498
+
4499
+ /**
4500
+ * Register a callback to be invoked whenever the specified account changes
4501
+ *
4502
+ * @param publicKey Public key of the account to monitor
4503
+ * @param callback Function to invoke whenever the account is changed
4504
+ * @param commitment Specify the commitment level account changes must reach before notification
4505
+ * @return subscription id
4506
+ */
4507
+ onAccountChange(
4508
+ publicKey: PublicKey,
4509
+ callback: AccountChangeCallback,
4510
+ commitment?: Commitment,
4511
+ ): ClientSubscriptionId {
4512
+ return this._makeSubscription({
4513
+ callback,
4514
+ method: 'accountSubscribe',
4515
+ params: {
4516
+ commitment,
4517
+ publicKey: publicKey.toBase58(),
4518
+ },
4519
+ unsubscribeMethod: 'accountUnsubscribe',
4520
+ });
4521
+ }
4522
+
4523
+ /**
4524
+ * Deregister an account notification callback
4525
+ *
4526
+ * @param id client subscription id to deregister
4527
+ */
4528
+ async removeAccountChangeListener(
4529
+ clientSubscriptionId: ClientSubscriptionId,
4530
+ ): Promise<void> {
4531
+ await this._unsubscribeClientSubscription(
4532
+ clientSubscriptionId,
4533
+ 'account change',
4534
+ );
4535
+ }
4536
+
4537
+ /**
4538
+ * @internal
4539
+ */
4540
+ _wsOnProgramAccountNotification(notification: Object) {
4541
+ const {result, subscription} = create(
4542
+ notification,
4543
+ ProgramAccountNotificationResult,
4544
+ );
4545
+ this._publishNotification<ProgramAccountChangeCallback>(subscription, [
4546
+ {
4547
+ accountId: result.value.pubkey,
4548
+ accountInfo: result.value.account,
4549
+ },
4550
+ result.context,
4551
+ ]);
4552
+ }
4553
+
4554
+ /**
4555
+ * Register a callback to be invoked whenever accounts owned by the
4556
+ * specified program change
4557
+ *
4558
+ * @param programId Public key of the program to monitor
4559
+ * @param callback Function to invoke whenever the account is changed
4560
+ * @param commitment Specify the commitment level account changes must reach before notification
4561
+ * @param filters The program account filters to pass into the RPC method
4562
+ * @return subscription id
4563
+ */
4564
+ onProgramAccountChange(
4565
+ programId: PublicKey,
4566
+ callback: ProgramAccountChangeCallback,
4567
+ commitment?: Commitment,
4568
+ filters?: GetProgramAccountsFilter[],
4569
+ ): ClientSubscriptionId {
4570
+ return this._makeSubscription({
4571
+ callback,
4572
+ method: 'programSubscribe',
4573
+ params: {
4574
+ commitment,
4575
+ filters,
4576
+ programId: programId.toBase58(),
4577
+ },
4578
+ unsubscribeMethod: 'programUnsubscribe',
4579
+ });
4580
+ }
4581
+
4582
+ /**
4583
+ * Deregister an account notification callback
4584
+ *
4585
+ * @param id client subscription id to deregister
4586
+ */
4587
+ async removeProgramAccountChangeListener(
4588
+ clientSubscriptionId: ClientSubscriptionId,
4589
+ ): Promise<void> {
4590
+ await this._unsubscribeClientSubscription(
4591
+ clientSubscriptionId,
4592
+ 'program account change',
4593
+ );
4594
+ }
4595
+
4596
+ /**
4597
+ * Registers a callback to be invoked whenever logs are emitted.
4598
+ */
4599
+ onLogs(
4600
+ filter: LogsFilter,
4601
+ callback: LogsCallback,
4602
+ commitment?: Commitment,
4603
+ ): ClientSubscriptionId {
4604
+ return this._makeSubscription({
4605
+ callback,
4606
+ method: 'logsSubscribe',
4607
+ params: {
4608
+ commitment,
4609
+ filter,
4610
+ },
4611
+ unsubscribeMethod: 'logsUnsubscribe',
4612
+ });
4613
+ }
4614
+
4615
+ /**
4616
+ * Deregister a logs callback.
4617
+ *
4618
+ * @param id client subscription id to deregister.
4619
+ */
4620
+ async removeOnLogsListener(
4621
+ clientSubscriptionId: ClientSubscriptionId,
4622
+ ): Promise<void> {
4623
+ await this._unsubscribeClientSubscription(clientSubscriptionId, 'logs');
4624
+ }
4625
+
4626
+ /**
4627
+ * @internal
4628
+ */
4629
+ _wsOnLogsNotification(notification: Object) {
4630
+ const {result, subscription} = create(notification, LogsNotificationResult);
4631
+ this._publishNotification<LogsCallback>(subscription, [
4632
+ result.value,
4633
+ result.context,
4634
+ ]);
4635
+ }
4636
+
4637
+ /**
4638
+ * @internal
4639
+ */
4640
+ _wsOnSlotNotification(notification: Object) {
4641
+ const {result, subscription} = create(notification, SlotNotificationResult);
4642
+ this._publishNotification<SlotChangeCallback>(subscription, [result]);
4643
+ }
4644
+
4645
+ /**
4646
+ * Register a callback to be invoked upon slot changes
4647
+ *
4648
+ * @param callback Function to invoke whenever the slot changes
4649
+ * @return subscription id
4650
+ */
4651
+ onSlotChange(callback: SlotChangeCallback): ClientSubscriptionId {
4652
+ return this._makeSubscription({
4653
+ callback,
4654
+ method: 'slotSubscribe',
4655
+ params: undefined,
4656
+ unsubscribeMethod: 'slotUnsubscribe',
4657
+ });
4658
+ }
4659
+
4660
+ /**
4661
+ * Deregister a slot notification callback
4662
+ *
4663
+ * @param id client subscription id to deregister
4664
+ */
4665
+ async removeSlotChangeListener(
4666
+ clientSubscriptionId: ClientSubscriptionId,
4667
+ ): Promise<void> {
4668
+ await this._unsubscribeClientSubscription(
4669
+ clientSubscriptionId,
4670
+ 'slot change',
4671
+ );
4672
+ }
4673
+
4674
+ /**
4675
+ * @internal
4676
+ */
4677
+ _wsOnSlotUpdatesNotification(notification: Object) {
4678
+ const {result, subscription} = create(
4679
+ notification,
4680
+ SlotUpdateNotificationResult,
4681
+ );
4682
+ this._publishNotification<SlotUpdateCallback>(subscription, [result]);
4683
+ }
4684
+
4685
+ /**
4686
+ * Register a callback to be invoked upon slot updates. {@link SlotUpdate}'s
4687
+ * may be useful to track live progress of a cluster.
4688
+ *
4689
+ * @param callback Function to invoke whenever the slot updates
4690
+ * @return subscription id
4691
+ */
4692
+ onSlotUpdate(callback: SlotUpdateCallback): ClientSubscriptionId {
4693
+ return this._makeSubscription({
4694
+ callback,
4695
+ method: 'slotsUpdatesSubscribe',
4696
+ params: undefined,
4697
+ unsubscribeMethod: 'slotsUpdatesUnsubscribe',
4698
+ });
4699
+ }
4700
+
4701
+ /**
4702
+ * Deregister a slot update notification callback
4703
+ *
4704
+ * @param id client subscription id to deregister
4705
+ */
4706
+ async removeSlotUpdateListener(
4707
+ clientSubscriptionId: ClientSubscriptionId,
4708
+ ): Promise<void> {
4709
+ await this._unsubscribeClientSubscription(
4710
+ clientSubscriptionId,
4711
+ 'slot update',
4712
+ );
4713
+ }
4714
+
4715
+ /**
4716
+ * @internal
4717
+ */
4718
+
4719
+ async _unsubscribeClientSubscription(
4720
+ clientSubscriptionId: ClientSubscriptionId,
4721
+ subscriptionName: string,
4722
+ ) {
4723
+ const dispose =
4724
+ this._subscriptionDisposeFunctionsByClientSubscriptionId[
4725
+ clientSubscriptionId
4726
+ ];
4727
+ if (dispose) {
4728
+ await dispose();
4729
+ } else {
4730
+ throw new Error(
4731
+ `Unknown ${subscriptionName} client subscription id: ${clientSubscriptionId}`,
4732
+ );
4733
+ }
4734
+ }
4735
+
4736
+ _buildArgs(
4737
+ args: Array<any>,
4738
+ override?: Commitment,
4739
+ encoding?: 'jsonParsed' | 'base64',
4740
+ extra?: any,
4741
+ ): Array<any> {
4742
+ const commitment = override || this._commitment;
4743
+ if (commitment || encoding || extra) {
4744
+ let options: any = {};
4745
+ if (encoding) {
4746
+ options.encoding = encoding;
4747
+ }
4748
+ if (commitment) {
4749
+ options.commitment = commitment;
4750
+ }
4751
+ if (extra) {
4752
+ options = Object.assign(options, extra);
4753
+ }
4754
+ args.push(options);
4755
+ }
4756
+ return args;
4757
+ }
4758
+
4759
+ /**
4760
+ * @internal
4761
+ */
4762
+ _buildArgsAtLeastConfirmed(
4763
+ args: Array<any>,
4764
+ override?: Finality,
4765
+ encoding?: 'jsonParsed' | 'base64',
4766
+ extra?: any,
4767
+ ): Array<any> {
4768
+ const commitment = override || this._commitment;
4769
+ if (commitment && !['confirmed', 'finalized'].includes(commitment)) {
4770
+ throw new Error(
4771
+ 'Using Connection with default commitment: `' +
4772
+ this._commitment +
4773
+ '`, but method requires at least `confirmed`',
4774
+ );
4775
+ }
4776
+ return this._buildArgs(args, override, encoding, extra);
4777
+ }
4778
+
4779
+ /**
4780
+ * @internal
4781
+ */
4782
+ _wsOnSignatureNotification(notification: Object) {
4783
+ const {result, subscription} = create(
4784
+ notification,
4785
+ SignatureNotificationResult,
4786
+ );
4787
+ if (result.value !== 'receivedSignature') {
4788
+ /**
4789
+ * Special case.
4790
+ * After a signature is processed, RPCs automatically dispose of the
4791
+ * subscription on the server side. We need to track which of these
4792
+ * subscriptions have been disposed in such a way, so that we know
4793
+ * whether the client is dealing with a not-yet-processed signature
4794
+ * (in which case we must tear down the server subscription) or an
4795
+ * already-processed signature (in which case the client can simply
4796
+ * clear out the subscription locally without telling the server).
4797
+ *
4798
+ * NOTE: There is a proposal to eliminate this special case, here:
4799
+ * https://github.com/solana-labs/solana/issues/18892
4800
+ */
4801
+ this._subscriptionsAutoDisposedByRpc.add(subscription);
4802
+ }
4803
+ this._publishNotification<SignatureSubscriptionCallback>(
4804
+ subscription,
4805
+ result.value === 'receivedSignature'
4806
+ ? [{type: 'received'}, result.context]
4807
+ : [{type: 'status', result: result.value}, result.context],
4808
+ );
4809
+ }
4810
+
4811
+ /**
4812
+ * Register a callback to be invoked upon signature updates
4813
+ *
4814
+ * @param signature Transaction signature string in base 58
4815
+ * @param callback Function to invoke on signature notifications
4816
+ * @param commitment Specify the commitment level signature must reach before notification
4817
+ * @return subscription id
4818
+ */
4819
+ onSignature(
4820
+ signature: TransactionSignature,
4821
+ callback: SignatureResultCallback,
4822
+ commitment?: Commitment,
4823
+ ): ClientSubscriptionId {
4824
+ const clientSubscriptionId = this._makeSubscription({
4825
+ callback: (notification, context) => {
4826
+ if (notification.type === 'status') {
4827
+ callback(notification.result, context);
4828
+ // Signatures subscriptions are auto-removed by the RPC service
4829
+ // so no need to explicitly send an unsubscribe message.
4830
+ try {
4831
+ this.removeSignatureListener(clientSubscriptionId);
4832
+ // eslint-disable-next-line no-empty
4833
+ } catch {
4834
+ // Already removed.
4835
+ }
4836
+ }
4837
+ },
4838
+ method: 'signatureSubscribe',
4839
+ params: {
4840
+ options: {commitment},
4841
+ signature,
4842
+ },
4843
+ unsubscribeMethod: 'signatureUnsubscribe',
4844
+ });
4845
+ return clientSubscriptionId;
4846
+ }
4847
+
4848
+ /**
4849
+ * Register a callback to be invoked when a transaction is
4850
+ * received and/or processed.
4851
+ *
4852
+ * @param signature Transaction signature string in base 58
4853
+ * @param callback Function to invoke on signature notifications
4854
+ * @param options Enable received notifications and set the commitment
4855
+ * level that signature must reach before notification
4856
+ * @return subscription id
4857
+ */
4858
+ onSignatureWithOptions(
4859
+ signature: TransactionSignature,
4860
+ callback: SignatureSubscriptionCallback,
4861
+ options?: SignatureSubscriptionOptions,
4862
+ ): ClientSubscriptionId {
4863
+ const clientSubscriptionId = this._makeSubscription({
4864
+ callback: (notification, context) => {
4865
+ callback(notification, context);
4866
+ // Signatures subscriptions are auto-removed by the RPC service
4867
+ // so no need to explicitly send an unsubscribe message.
4868
+ try {
4869
+ this.removeSignatureListener(clientSubscriptionId);
4870
+ // eslint-disable-next-line no-empty
4871
+ } catch {
4872
+ // Already removed.
4873
+ }
4874
+ },
4875
+ method: 'signatureSubscribe',
4876
+ params: {
4877
+ options,
4878
+ signature,
4879
+ },
4880
+ unsubscribeMethod: 'signatureUnsubscribe',
4881
+ });
4882
+ return clientSubscriptionId;
4883
+ }
4884
+
4885
+ /**
4886
+ * Deregister a signature notification callback
4887
+ *
4888
+ * @param id client subscription id to deregister
4889
+ */
4890
+ async removeSignatureListener(
4891
+ clientSubscriptionId: ClientSubscriptionId,
4892
+ ): Promise<void> {
4893
+ await this._unsubscribeClientSubscription(
4894
+ clientSubscriptionId,
4895
+ 'signature result',
4896
+ );
4897
+ }
4898
+
4899
+ /**
4900
+ * @internal
4901
+ */
4902
+ _wsOnRootNotification(notification: Object) {
4903
+ const {result, subscription} = create(notification, RootNotificationResult);
4904
+ this._publishNotification<RootChangeCallback>(subscription, [result]);
4905
+ }
4906
+
4907
+ /**
4908
+ * Register a callback to be invoked upon root changes
4909
+ *
4910
+ * @param callback Function to invoke whenever the root changes
4911
+ * @return subscription id
4912
+ */
4913
+ onRootChange(callback: RootChangeCallback): ClientSubscriptionId {
4914
+ return this._makeSubscription({
4915
+ callback,
4916
+ method: 'rootSubscribe',
4917
+ params: undefined,
4918
+ unsubscribeMethod: 'rootUnsubscribe',
4919
+ });
4920
+ }
4921
+
4922
+ /**
4923
+ * Deregister a root notification callback
4924
+ *
4925
+ * @param id client subscription id to deregister
4926
+ */
4927
+ async removeRootChangeListener(
4928
+ clientSubscriptionId: ClientSubscriptionId,
4929
+ ): Promise<void> {
4930
+ await this._unsubscribeClientSubscription(
4931
+ clientSubscriptionId,
4932
+ 'root change',
4933
+ );
4934
+ }
4935
+ }