@solana/rpc-graphql 2.0.0-20241006045741

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 (79) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +1127 -0
  3. package/dist/index.browser.cjs +5169 -0
  4. package/dist/index.browser.cjs.map +1 -0
  5. package/dist/index.browser.mjs +5159 -0
  6. package/dist/index.browser.mjs.map +1 -0
  7. package/dist/index.native.mjs +5159 -0
  8. package/dist/index.native.mjs.map +1 -0
  9. package/dist/index.node.cjs +5169 -0
  10. package/dist/index.node.cjs.map +1 -0
  11. package/dist/index.node.mjs +5159 -0
  12. package/dist/index.node.mjs.map +1 -0
  13. package/dist/types/context.d.ts +20 -0
  14. package/dist/types/context.d.ts.map +1 -0
  15. package/dist/types/index.d.ts +29 -0
  16. package/dist/types/index.d.ts.map +1 -0
  17. package/dist/types/loaders/account.d.ts +14 -0
  18. package/dist/types/loaders/account.d.ts.map +1 -0
  19. package/dist/types/loaders/block.d.ts +4 -0
  20. package/dist/types/loaders/block.d.ts.map +1 -0
  21. package/dist/types/loaders/coalescer.d.ts +55 -0
  22. package/dist/types/loaders/coalescer.d.ts.map +1 -0
  23. package/dist/types/loaders/index.d.ts +6 -0
  24. package/dist/types/loaders/index.d.ts.map +1 -0
  25. package/dist/types/loaders/loader.d.ts +85 -0
  26. package/dist/types/loaders/loader.d.ts.map +1 -0
  27. package/dist/types/loaders/program-accounts.d.ts +8 -0
  28. package/dist/types/loaders/program-accounts.d.ts.map +1 -0
  29. package/dist/types/loaders/transaction.d.ts +4 -0
  30. package/dist/types/loaders/transaction.d.ts.map +1 -0
  31. package/dist/types/resolvers/account.d.ts +36 -0
  32. package/dist/types/resolvers/account.d.ts.map +1 -0
  33. package/dist/types/resolvers/block.d.ts +68 -0
  34. package/dist/types/resolvers/block.d.ts.map +1 -0
  35. package/dist/types/resolvers/program-accounts.d.ts +21 -0
  36. package/dist/types/resolvers/program-accounts.d.ts.map +1 -0
  37. package/dist/types/resolvers/resolve-info/account.d.ts +15 -0
  38. package/dist/types/resolvers/resolve-info/account.d.ts.map +1 -0
  39. package/dist/types/resolvers/resolve-info/block.d.ts +13 -0
  40. package/dist/types/resolvers/resolve-info/block.d.ts.map +1 -0
  41. package/dist/types/resolvers/resolve-info/index.d.ts +6 -0
  42. package/dist/types/resolvers/resolve-info/index.d.ts.map +1 -0
  43. package/dist/types/resolvers/resolve-info/program-accounts.d.ts +23 -0
  44. package/dist/types/resolvers/resolve-info/program-accounts.d.ts.map +1 -0
  45. package/dist/types/resolvers/resolve-info/transaction.d.ts +15 -0
  46. package/dist/types/resolvers/resolve-info/transaction.d.ts.map +1 -0
  47. package/dist/types/resolvers/resolve-info/visitor.d.ts +18 -0
  48. package/dist/types/resolvers/resolve-info/visitor.d.ts.map +1 -0
  49. package/dist/types/resolvers/transaction.d.ts +38 -0
  50. package/dist/types/resolvers/transaction.d.ts.map +1 -0
  51. package/dist/types/schema/type-defs/account.d.ts +2 -0
  52. package/dist/types/schema/type-defs/account.d.ts.map +1 -0
  53. package/dist/types/schema/type-defs/block.d.ts +2 -0
  54. package/dist/types/schema/type-defs/block.d.ts.map +1 -0
  55. package/dist/types/schema/type-defs/index.d.ts +7 -0
  56. package/dist/types/schema/type-defs/index.d.ts.map +1 -0
  57. package/dist/types/schema/type-defs/instruction.d.ts +2 -0
  58. package/dist/types/schema/type-defs/instruction.d.ts.map +1 -0
  59. package/dist/types/schema/type-defs/root.d.ts +2 -0
  60. package/dist/types/schema/type-defs/root.d.ts.map +1 -0
  61. package/dist/types/schema/type-defs/transaction.d.ts +2 -0
  62. package/dist/types/schema/type-defs/transaction.d.ts.map +1 -0
  63. package/dist/types/schema/type-defs/types.d.ts +2 -0
  64. package/dist/types/schema/type-defs/types.d.ts.map +1 -0
  65. package/dist/types/schema/type-resolvers/account.d.ts +576 -0
  66. package/dist/types/schema/type-resolvers/account.d.ts.map +1 -0
  67. package/dist/types/schema/type-resolvers/block.d.ts +7 -0
  68. package/dist/types/schema/type-resolvers/block.d.ts.map +1 -0
  69. package/dist/types/schema/type-resolvers/index.d.ts +8 -0
  70. package/dist/types/schema/type-resolvers/index.d.ts.map +1 -0
  71. package/dist/types/schema/type-resolvers/instruction.d.ts +3549 -0
  72. package/dist/types/schema/type-resolvers/instruction.d.ts.map +1 -0
  73. package/dist/types/schema/type-resolvers/root.d.ts +3 -0
  74. package/dist/types/schema/type-resolvers/root.d.ts.map +1 -0
  75. package/dist/types/schema/type-resolvers/transaction.d.ts +8 -0
  76. package/dist/types/schema/type-resolvers/transaction.d.ts.map +1 -0
  77. package/dist/types/schema/type-resolvers/types.d.ts +154 -0
  78. package/dist/types/schema/type-resolvers/types.d.ts.map +1 -0
  79. package/package.json +90 -0
package/README.md ADDED
@@ -0,0 +1,1127 @@
1
+ # @solana/rpc-graphql
2
+
3
+ This package defines a GraphQL client resolver built on top of the
4
+ [Solana JSON-RPC](https://docs.solana.com/api/http).
5
+
6
+ A client resolver in this context is simply a client-side RPC interface
7
+ designed to give application developers the ability to use GraphQL to interact
8
+ with data on the Solana blockchain.
9
+
10
+ The resolver presents developers with a new schema for working with Solana data
11
+ (see [Schema](#schema)), as well as new features only possible with GraphQL.
12
+ Additionally, the resolver is designed to make highly-optimized use of the
13
+ Solana JSON RPC, balancing RPC requests, batch loading, and caching
14
+ (see [RPC Optimizations](#rpc-optimizations)).
15
+
16
+ GraphQL is a query language for your API, and a server-side runtime for
17
+ executing queries using a type system you define for your data.
18
+
19
+ <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/GraphQL_Logo.svg/1024px-GraphQL_Logo.svg.png?20161105194737" alt="graphql-icon" width="24" align="center"/> [**GraphQL**](https://graphql.org/learn/)
20
+
21
+ # Quick Start
22
+
23
+ The RPC-GraphQL client requires an RPC client, as defined by the package
24
+ `@solana/rpc-spec`. Such a client is available in `@solana/web3.js:2.0` or
25
+ can be created manually with a custom implementation.
26
+
27
+ ```ts
28
+ Rpc<TRpcMethods>;
29
+ ```
30
+
31
+ The RPC-GraphQL requires an RPC client with the following API methods available
32
+ for use in order to properly execute all queries.
33
+
34
+ ```ts
35
+ Rpc<GetAccountInfoApi & GetBlockApi & GetMultipleAccountsApi & GetProgramAccountsApi & GetTransactionApi>;
36
+ ```
37
+
38
+ To initialize the RPC-GraphQL client, simple use `createSolanaRpcGraphQL`.
39
+
40
+ ```ts
41
+ import { createSolanaRpc } from '@solana/rpc';
42
+
43
+ // Create the RPC client
44
+ const rpc = createSolanaRpc('https://api.devnet.solana.com');
45
+
46
+ // Create the RPC-GraphQL client
47
+ const rpcGraphQL = createSolanaRpcGraphQL(rpc);
48
+ ```
49
+
50
+ The `RpcGraphQL` type supports one method `query` which accepts a string
51
+ query source and an optional `variableValues` parameter - which is an object
52
+ containing any variables to pipe into the query string.
53
+
54
+ You can define queries with hard-coded parameters.
55
+
56
+ ```ts
57
+ const source = `
58
+ query myQuery {
59
+ account(address: "AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca") {
60
+ lamports
61
+ }
62
+ }
63
+ `;
64
+
65
+ const result = await rpcGraphQL.query(source);
66
+ ```
67
+
68
+ ```
69
+ data: {
70
+ account: {
71
+ lamports: 10290815n,
72
+ },
73
+ }
74
+ ```
75
+
76
+ You can also pass the variable values.
77
+
78
+ ```ts
79
+ const source = `
80
+ query myQuery($address: String!) {
81
+ account(address: $address) {
82
+ lamports
83
+ }
84
+ }
85
+ `;
86
+
87
+ const variableValues = {
88
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
89
+ };
90
+
91
+ const result = await rpcGraphQL.query(source, variableValues);
92
+ ```
93
+
94
+ ```
95
+ data: {
96
+ account: {
97
+ lamports: 10290815n,
98
+ },
99
+ }
100
+ ```
101
+
102
+ Queries with variable values can also be re-used!
103
+
104
+ ```ts
105
+ const source = `
106
+ query myQuery($address: String!) {
107
+ account(address: $address) {
108
+ lamports
109
+ }
110
+ }
111
+ `;
112
+
113
+ const lamportsAccountA = await rpcGraphQL.query(source, {
114
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
115
+ });
116
+
117
+ const lamportsAccountB = await rpcGraphQL.query(source, {
118
+ address: 'CcYNb7WqpjaMrNr7B1mapaNfWctZRH7LyAjWRLBGt1Fk',
119
+ });
120
+ ```
121
+
122
+ # Schema
123
+
124
+ Solana data can be categorized into three main types:
125
+
126
+ - Accounts
127
+ - Transactions
128
+ - Blocks
129
+
130
+ These types encompass everything that can be queried from the Solana ledger.
131
+
132
+ ## Accounts
133
+
134
+ The `Account` interface contains common fields across all accounts.
135
+
136
+ ```graphql
137
+ interface Account {
138
+ address: Address
139
+ data(encoding: AccountEncoding!, dataSlice: DataSlice): String
140
+ executable: Boolean
141
+ lamports: BigInt
142
+ ownerProgram: Account
143
+ space: BigInt
144
+ rentEpoch: BigInt
145
+ }
146
+ ```
147
+
148
+ Any account can be queried by these fields without specifying the specific
149
+ account type.
150
+
151
+ ```ts
152
+ const source = `
153
+ query myQuery($address: String!) {
154
+ account(address: $address) {
155
+ executable
156
+ lamports
157
+ rentEpoch
158
+ }
159
+ }
160
+ `;
161
+
162
+ const variableValues = {
163
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
164
+ };
165
+
166
+ const result = await rpcGraphQL.query(source, variableValues);
167
+ ```
168
+
169
+ ```
170
+ data: {
171
+ account: {
172
+ executable: false,
173
+ lamports: 10290815n,
174
+ rentEpoch: 0n,
175
+ },
176
+ }
177
+ ```
178
+
179
+ ### Querying Account Data
180
+
181
+ Querying accounts by their encoded data (`base58`, `base64`, `base64+zstd`) is
182
+ still fully supported.
183
+
184
+ ```ts
185
+ const source = `
186
+ query myQuery($address: String!) {
187
+ account(address: $address) {
188
+ data(encoding: BASE_64)
189
+ }
190
+ }
191
+ `;
192
+
193
+ const variableValues = {
194
+ address: 'CcYNb7WqpjaMrNr7B1mapaNfWctZRH7LyAjWRLBGt1Fk',
195
+ };
196
+
197
+ const result = await rpcGraphQL.query(source, variableValues);
198
+ ```
199
+
200
+ ```
201
+ data: {
202
+ account: {
203
+ data: 'dGVzdCBkYXRh',
204
+ },
205
+ }
206
+ ```
207
+
208
+ ### Querying Specific Account Types
209
+
210
+ A set of specific parsed account types are supported in GraphQL.
211
+
212
+ - `GenericAccount`: A generic base account type
213
+ - `NonceAccount`: A nonce account
214
+ - `LookupTableAccount`: An address lookup table account
215
+ - `MintAccount`: An SPL mint
216
+ - `TokenAccount`: An SPL token account
217
+ - `StakeAccount`: A stake account
218
+ - `VoteAccount`: A vote account
219
+
220
+ You can choose how to handle querying of specific account types. For example,
221
+ you might _only_ want specifically any account that matches `MintAccount`.
222
+
223
+ ```ts
224
+ const maybeMintAddresses = [
225
+ 'J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ',
226
+ 'JAbWqZ7S2c6jomQr8ofAYBo257bE1QJtHwbX1yWc2osZ',
227
+ '2AQ4CSNu6zNUZsUq4aLNUSjyrLv4qFFXQuKs5RTHbg2Y',
228
+ 'EVW3CoyogapBfQxBFFEKGMM1bn3JyoFiqkAJdw3FHX1b',
229
+ ];
230
+
231
+ const mintAccounts = [];
232
+
233
+ const source = `
234
+ query myQuery($address: String!) {
235
+ account(address: $address) {
236
+ ... on MintAccount {
237
+ data {
238
+ decimals
239
+ isInitialized
240
+ mintAuthority
241
+ supply
242
+ }
243
+ }
244
+ }
245
+ }
246
+ `;
247
+
248
+ for (const address of maybeMintAddresses) {
249
+ const result = await rpcGraphQL.query(source, { address });
250
+ if (result != null) {
251
+ const {
252
+ data: {
253
+ account: { data: mintInfo },
254
+ },
255
+ } = result;
256
+ mintAccounts.push(mintInfo);
257
+ }
258
+ }
259
+ ```
260
+
261
+ Maybe you want to handle both mints _and_ token accounts.
262
+
263
+ ```ts
264
+ const mintOrTokenAccountAddresses = [
265
+ 'J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ',
266
+ 'JAbWqZ7S2c6jomQr8ofAYBo257bE1QJtHwbX1yWc2osZ',
267
+ '2AQ4CSNu6zNUZsUq4aLNUSjyrLv4qFFXQuKs5RTHbg2Y',
268
+ 'EVW3CoyogapBfQxBFFEKGMM1bn3JyoFiqkAJdw3FHX1b',
269
+ ];
270
+
271
+ const mintAccounts = [];
272
+ const tokenAccounts = [];
273
+
274
+ const source = `
275
+ query myQuery($address: String!) {
276
+ account(address: $address) {
277
+ ... on MintAccount {
278
+ __typename
279
+ decimals
280
+ isInitialized
281
+ supply
282
+ }
283
+ ... on TokenAccount {
284
+ __typename
285
+ isNative
286
+ mint
287
+ state
288
+ }
289
+ }
290
+ }
291
+ `;
292
+
293
+ for (const address of mintOrTokenAccountAddresses) {
294
+ const result = await rpcGraphQL.query(source, { address });
295
+ if (result != null) {
296
+ const {
297
+ data: { account: accountParsedData },
298
+ } = result;
299
+ if (accountParsedData.__typename === 'MintAccount') {
300
+ mintAccounts.push(accountParsedInfo);
301
+ } else {
302
+ tokenAccounts.push(accountParsedInfo);
303
+ }
304
+ }
305
+ }
306
+ ```
307
+
308
+ ### Querying Program Accounts
309
+
310
+ Another account-based query that can be performed with RPC-GraphQL is the
311
+ `programAccounts` query. The response will be a list of `Account` types as
312
+ defined above.
313
+
314
+ ```ts
315
+ const source = `
316
+ query myQuery($programAddress: String!) {
317
+ programAccounts(programAddress: $address) {
318
+ executable
319
+ lamports
320
+ rentEpoch
321
+ }
322
+ }
323
+ `;
324
+
325
+ const variableValues = {
326
+ programAddress: 'AmtpVzo6H6qQCP9dH9wfu5hfa8kKaAFpTJ4aamPYR6V6',
327
+ };
328
+
329
+ const result = await rpcGraphQL.query(source, variableValues);
330
+ ```
331
+
332
+ ```
333
+ data: {
334
+ programAccounts: [
335
+ {
336
+ executable: false,
337
+ lamports: 10290815n,
338
+ rentEpoch: 0n,
339
+ },
340
+ {
341
+ executable: false,
342
+ lamports: 10290815n,
343
+ rentEpoch: 0n,
344
+ },
345
+ /* .. */
346
+ ]
347
+ }
348
+ ```
349
+
350
+ Account data encoding in `base58`, `base64`, and `base64+zstd` is also
351
+ supported with this query, as well as `dataSlice` and `filter`.
352
+
353
+ ```ts
354
+ const source = `
355
+ query myQuery($programAddress: String!) {
356
+ programAccounts(programAddress: $programAddress) {
357
+ data(encoding: BASE_64, dataSlice: { length: 5, offset: 0 })
358
+ }
359
+ }
360
+ `;
361
+
362
+ const variableValues = {
363
+ programAddress: 'DXngmJfjurhnAwbMPgpUGPH6qNvetCKRJ6PiD4ag4PTj',
364
+ };
365
+
366
+ const result = await rpcGraphQL.query(source, variableValues);
367
+ ```
368
+
369
+ ```
370
+ data: {
371
+ programAccounts: [
372
+ {
373
+ data: 'dGVzdCA=',
374
+ },
375
+ /* .. */
376
+ ],
377
+ }
378
+ ```
379
+
380
+ Although specific parsed account types are directly tied to the program which
381
+ owns them, it's still possible to handle various specific account types within
382
+ the same program accounts response.
383
+
384
+ ```ts
385
+ const source = `
386
+ query myQuery($programAddress: String!) {
387
+ programAccounts(programAddress: $address) {
388
+ ... on MintAccount {
389
+ __typename
390
+ decimals
391
+ isInitialized
392
+ mintAuthority
393
+ supply
394
+ }
395
+ ... on TokenAccount {
396
+ __typename
397
+ isNative
398
+ mint
399
+ owner
400
+ state
401
+ }
402
+ }
403
+ }
404
+ `;
405
+
406
+ const variableValues = {
407
+ programAddress: 'AmtpVzo6H6qQCP9dH9wfu5hfa8kKaAFpTJ4aamPYR6V6',
408
+ };
409
+
410
+ const result = await rpcGraphQL.query(source, variableValues);
411
+
412
+ const { mints, tokenAccounts } = result.data.programAccounts.reduce(
413
+ (acc: { mints: any[]; tokenAccounts: any[] }, account) => {
414
+ if (account.__typename === 'MintAccount') {
415
+ acc.mints.push(accountParsedInfo);
416
+ } else {
417
+ acc.tokenAccounts.push(accountParsedInfo);
418
+ }
419
+ return acc;
420
+ },
421
+ { mints: [], tokenAccounts: [] },
422
+ );
423
+ ```
424
+
425
+ ### Nested Account Queries
426
+
427
+ Notice the `owner` field of the `Account` interface is also an `Account`
428
+ interface. This powers nested queries against the `owner` field of an account.
429
+
430
+ ```ts
431
+ const source = `
432
+ query myQuery($address: String!) {
433
+ account(address: $address) {
434
+ address
435
+ owner {
436
+ address
437
+ executable
438
+ lamports
439
+ }
440
+ }
441
+ }
442
+ `;
443
+
444
+ const variableValues = {
445
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
446
+ };
447
+
448
+ const result = await rpcGraphQL.query(source, variableValues);
449
+ ```
450
+
451
+ ```
452
+ data: {
453
+ account: {
454
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
455
+ owner: {
456
+ address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
457
+ executable: true,
458
+ lamports: 10290815n,
459
+ },
460
+ },
461
+ }
462
+ ```
463
+
464
+ As you can see, simply defining a nested query with RPC-GraphQL will augment
465
+ the multiple RPC calls and parsing code required to gather the necessary
466
+ information!
467
+
468
+ You can nest queries as far as you want!
469
+
470
+ ```ts
471
+ const source = `
472
+ query myQuery($address: String!) {
473
+ account(address: $address) {
474
+ address
475
+ owner {
476
+ address
477
+ owner {
478
+ address
479
+ owner {
480
+ address
481
+ }
482
+ }
483
+ }
484
+ }
485
+ }
486
+ `;
487
+
488
+ const variableValues = {
489
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
490
+ };
491
+
492
+ const result = await rpcGraphQL.query(source, variableValues);
493
+ ```
494
+
495
+ ```
496
+ data: {
497
+ account: {
498
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
499
+ owner: {
500
+ address: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
501
+ owner: {
502
+ address: 'BPFLoader2111111111111111111111111111111111',
503
+ owner: {
504
+ address: 'NativeLoader1111111111111111111111111111111',
505
+ },
506
+ },
507
+ },
508
+ },
509
+ }
510
+ ```
511
+
512
+ Nested queries can also be applied to specific account types.
513
+
514
+ ```ts
515
+ const source = `
516
+ query myQuery($address: String!) {
517
+ account(address: $address) {
518
+ ... on MintAccount {
519
+ address
520
+ data {
521
+ mintAuthority {
522
+ address
523
+ lamports
524
+ }
525
+ }
526
+ }
527
+ }
528
+ }
529
+ `;
530
+
531
+ const variableValues = {
532
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
533
+ };
534
+
535
+ const result = await rpcGraphQL.query(source, variableValues);
536
+ ```
537
+
538
+ ```
539
+ data: {
540
+ account: {
541
+ address: 'AyGCwnwxQMCqaU4ixReHt8h5W4dwmxU7eM3BEQBdWVca',
542
+ data: {
543
+ mintAuthority: {
544
+ address: 'DpfJkNonoVB3sor9H9ceajhex4XHVPrDAGAq2ahdG4JZ',
545
+ lamports: 10290815n,
546
+ }
547
+ },
548
+ },
549
+ }
550
+ ```
551
+
552
+ Nested account queries are also supported on `programAccounts` queries.
553
+
554
+ ## Transactions
555
+
556
+ The `Transaction` type contains common fields across all transactions.
557
+
558
+ ```graphql
559
+ type Transaction {
560
+ blockTime: BigInt
561
+ data(encoding: TransactionEncoding!): String
562
+ message: TransactionMessage
563
+ meta: TransactionMeta
564
+ signatures: [Signature]
565
+ slot: Slot
566
+ version: String
567
+ }
568
+ ```
569
+
570
+ Note that unlike accounts, the `Transaction` type is not an interface, so the
571
+ base response type of a transaction query remains constant. However, the list
572
+ of instructions contained in a parsed transaction are returned as the
573
+ `TransactionInstruction` interface, which can be queried by specific type.
574
+ See [Querying Specific Transaction Instruction Types](#querying-specific-transaction-instruction-types).
575
+
576
+ ```graphql
577
+ interface TransactionInstruction {
578
+ programId: Address
579
+ }
580
+ ```
581
+
582
+ A transaction can be queried by the `transaction` query.
583
+
584
+ ```ts
585
+ const source = `
586
+ query myQuery($signature: String!) {
587
+ transaction(signature: $signature) {
588
+ blockTime
589
+ meta {
590
+ computeUnitsConsumed
591
+ logMessages
592
+ }
593
+ slot
594
+ }
595
+ }
596
+ `;
597
+
598
+ const variableValues = {
599
+ signature: '63zkpxATgAwXRGFQZPDESTw2m4uZQ99sX338ibgKtTcgG6v34E3MSS3zckCwJHrimS71cvei6h1Bn1K1De53BNWC',
600
+ };
601
+
602
+ const result = await rpcGraphQL.query(source, variableValues);
603
+ ```
604
+
605
+ ```
606
+ data: {
607
+ transaction: {
608
+ blockTime: 230860412n,
609
+ meta: {
610
+ computeUnitsConsumed: 120000n,
611
+ logMessages: [
612
+ "Program 8tfDNiaEyrV6Q1U4DEXrEigs9DoDtkugzFbybENEbCDz invoke [1]",
613
+ "Program 8tfDNiaEyrV6Q1U4DEXrEigs9DoDtkugzFbybENEbCDz consumed 2164 of 452155 compute units",
614
+ "Program 8tfDNiaEyrV6Q1U4DEXrEigs9DoDtkugzFbybENEbCDz success",
615
+ "Program ComputeBudget111111111111111111111111111111 invoke [1]",
616
+ "Program ComputeBudget111111111111111111111111111111 success"
617
+ ]
618
+ },
619
+ slot: 230860693n,
620
+ },
621
+ }
622
+ ```
623
+
624
+ ### Querying Transaction Data
625
+
626
+ Querying encoded transaction data (`base58`, `base64`) is fully supported.
627
+
628
+ ```ts
629
+ const source = `
630
+ query myQuery($signature: String!, $commitment: Commitment) {
631
+ transaction(signature: $signature, commitment: $commitment) {
632
+ data(encoding: BASE_64)
633
+ }
634
+ }
635
+ `;
636
+
637
+ const variableValues = {
638
+ signature: '63zkpxATgAwXRGFQZPDESTw2m4uZQ99sX338ibgKtTcgG6v34E3MSS3zckCwJHrimS71cvei6h1Bn1K1De53BNWC',
639
+ commitment: 'confirmed',
640
+ };
641
+
642
+ const result = await rpcGraphQL.query(source, variableValues);
643
+ ```
644
+
645
+ ```
646
+ {
647
+ "data": {
648
+ "transaction": {
649
+ "data": "AbgFjqLTBtoAaHXexSN1OYXf+UNox6qe3JcyCmEwE57iUHxCkHp8zKTJVznd6nLtUFNMYJWHCtMb+yPjk7QIxAQBAAEDeXJtpS2Z1gsH6tc7L28L9gg8yFx3qU401pHXj4vK/skUvD7y/LnapfCdSx3FfTguy49UDQVvGgOK0ix/P42YuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5uE0ATTHEABQrIB1+aoEdYJxvQthXPLHFxSH2y+ACK4BAgIAAQwCAAAAAMqaOwAAAAA="
650
+ }
651
+ }
652
+ }
653
+ ```
654
+
655
+ ### Querying Specific Transaction Instruction Types
656
+
657
+ As mentioned above, parsed transactions return a list of instructions that
658
+ implement the `TransactionInstruction` interface. These instructions can be
659
+ queried by specific instruction types.
660
+
661
+ Instructions for the following programs are supported.
662
+
663
+ - Address Lookup Table
664
+ - BPF Loader
665
+ - BPF Upgradeable Loader
666
+ - Stake
667
+ - SPL Associated Token
668
+ - SPL Memo
669
+ - SPL Token
670
+ - System
671
+ - Vote
672
+
673
+ Additionally, the `GenericInstruction` type is the base parsed instruction type.
674
+
675
+ ```graphql
676
+ type GenericInstruction implements TransactionInstruction {
677
+ accounts: [Address]
678
+ data: Base64EncodedBytes
679
+ programId: Address
680
+ }
681
+ ```
682
+
683
+ Specific transaction instruction types can be queried within a `Transaction`
684
+ response like so.
685
+
686
+ ```ts
687
+ const source = `
688
+ query myQuery($signature: String!, $commitment: Commitment) {
689
+ transaction(signature: $signature, commitment: $commitment) {
690
+ message {
691
+ instructions {
692
+ ... on CreateAccountInstruction {
693
+ lamports
694
+ programId
695
+ space
696
+ }
697
+ }
698
+ }
699
+ }
700
+ }
701
+ `;
702
+
703
+ const variableValues = {
704
+ signature: '63zkpxATgAwXRGFQZPDESTw2m4uZQ99sX338ibgKtTcgG6v34E3MSS3zckCwJHrimS71cvei6h1Bn1K1De53BNWC',
705
+ commitment: 'confirmed',
706
+ };
707
+
708
+ const result = await rpcGraphQL.query(source, variableValues);
709
+ ```
710
+
711
+ ```
712
+ data: {
713
+ transaction: {
714
+ message: {
715
+ instructions: [
716
+ {
717
+ lamports: 890880n,
718
+ programId: '11111111111111111111111111111111',
719
+ space: 0n,
720
+ },
721
+ /* .. */
722
+ ]
723
+ },
724
+ },
725
+ }
726
+ ```
727
+
728
+ ### Nested Transaction Queries
729
+
730
+ Since transactions have a relatively large number of data points, they are
731
+ particularly useful for nested queries!
732
+
733
+ Similar to nested querying accounts, it's possible to nest queries inside your
734
+ transaction queries to look up other objects, such as accounts, as they appear
735
+ in the transaction response.
736
+
737
+ ```ts
738
+ const source = `
739
+ query myQuery($signature: String!, $commitment: Commitment) {
740
+ transaction(signature: $signature, commitment: $commitment) {
741
+ message {
742
+ instructions {
743
+ ... on SplTokenTransferInstruction {
744
+ amount
745
+ authority {
746
+ # Account
747
+ address
748
+ lamports
749
+ }
750
+ destination {
751
+ # Account
752
+ ... on TokenAccount {
753
+ address
754
+ mint {
755
+ ... on MintAccount {
756
+ # Account
757
+ address
758
+ decimals
759
+ }
760
+ }
761
+ owner {
762
+ # Account
763
+ address
764
+ lamports
765
+ }
766
+ }
767
+ }
768
+ source {
769
+ # Account
770
+ ... on TokenAccount {
771
+ address
772
+ mint {
773
+ ... on MintAccount {
774
+ # Account
775
+ address
776
+ decimals
777
+ }
778
+ }
779
+ owner {
780
+ # Account
781
+ address
782
+ lamports
783
+ }
784
+ }
785
+ }
786
+ }
787
+ }
788
+ }
789
+ }
790
+ }
791
+ `;
792
+
793
+ const variableValues = {
794
+ signature: '63zkpxATgAwXRGFQZPDESTw2m4uZQ99sX338ibgKtTcgG6v34E3MSS3zckCwJHrimS71cvei6h1Bn1K1De53BNWC',
795
+ commitment: 'confirmed',
796
+ };
797
+
798
+ const result = await rpcGraphQL.query(source, variableValues);
799
+ ```
800
+
801
+ ```
802
+ data: {
803
+ transaction: {
804
+ message: {
805
+ instructions: [
806
+ {
807
+ amount: '50',
808
+ authority: {
809
+ address: 'AHPPMhzDQix9sKULBqeaQ5BUZgrKdz8tg6DzPxsofB12',
810
+ lamports: 890880n,
811
+ },
812
+ destination: {
813
+ address: '2W8mUY75zxqwAcpirn75r3Cc7TStMirFyHwKqo13fmB1',
814
+ mint: {
815
+ address: '8poKMotB2cEYVv5sbjrdyssASZj1vwYCe7GJFeXo2QP7',
816
+ decimals: 6,
817
+ },
818
+ owner: {
819
+ address: '7tRxJ2znbTFpwW9XaMMiDsXDudoPEUXRcpDpm8qjWgAZ',
820
+ lamports: 890880n,
821
+ }
822
+ },
823
+ source: {
824
+ address: 'BqFCPqXUm4cq6jaZZx1TDTvUR1wdEuNNwAHBEVR6mJhM',
825
+ mint: {
826
+ address: '8poKMotB2cEYVv5sbjrdyssASZj1vwYCe7GJFeXo2QP7',
827
+ decimals: 6,
828
+ },
829
+ owner: {
830
+ address: '3dPmVLMD7PC5faZNyJUH9WFrUxAsbjydJfoozwmR1wDG',
831
+ lamports: e890880n,
832
+ }
833
+ }
834
+ },
835
+ /* .. */
836
+ ]
837
+ }
838
+ }
839
+ }
840
+ ```
841
+
842
+ ## Blocks
843
+
844
+ The `Block` type contains common fields across all blocks.
845
+
846
+ ```graphql
847
+ type Block {
848
+ blockhash: String
849
+ blockHeight: BigInt
850
+ blockTime: BigInt
851
+ parentSlot: Slot
852
+ previousBlockhash: String
853
+ rewards: [Reward]
854
+ signatures: [Signature]
855
+ transactions: [Transaction]
856
+ }
857
+ ```
858
+
859
+ Just like the `programAccounts` query will return a list of `Account` types, on
860
+ which you can perform many operations, the `block` query will return a list of
861
+ `Transaction` types, however blocks also contain their own high-level data
862
+ fields, such as `blockhash` and `blockTime`.
863
+
864
+ ```ts
865
+ const source = `
866
+ query myQuery($slot: BigInt!, $commitment: Commitment) {
867
+ block(slot: $slot, commitment: $commitment) {
868
+ blockHeight
869
+ blockhash
870
+ parentSlot
871
+ rewards {
872
+ commission
873
+ lamports
874
+ rewardType
875
+ }
876
+ transactions {
877
+ message {
878
+ instructions {
879
+ ... on CreateAccountInstruction {
880
+ lamports
881
+ programId
882
+ space
883
+ }
884
+ }
885
+ }
886
+ }
887
+ }
888
+ }
889
+ `;
890
+
891
+ const variableValues = {
892
+ slot: 43596n,
893
+ commitment: 'confirmed',
894
+ };
895
+
896
+ const result = await rpcGraphQL.query(source, variableValues);
897
+ ```
898
+
899
+ ```
900
+ data: {
901
+ block: {
902
+ blockHeight: 196758578n,
903
+ blockhash: 'BqFCPqXUm4cq6jaZZx1TDTvUR1wdEuNNwAHBEVR6mJhM',
904
+ parentSlot: 230862408n,
905
+ rewards: [
906
+ {
907
+ commission: 0.05,
908
+ lamports: 58578n,
909
+ rewardType: 'staking',
910
+ },
911
+ {
912
+ commission: 0.05,
913
+ lamports: 58578n,
914
+ rewardType: 'staking',
915
+ }
916
+ ],
917
+ transactions: [
918
+ {
919
+ message: {
920
+ instructions: [
921
+ {
922
+ lamports: 890880n,
923
+ programId: '11111111111111111111111111111111',
924
+ space: 0n,
925
+ },
926
+ /* .. */
927
+ ]
928
+ },
929
+ }
930
+ ],
931
+ },
932
+ }
933
+ ```
934
+
935
+ # RPC Optimizations
936
+
937
+ RPC-GraphQL ships highly-optimized use of the Solana JSON RPC out of the box,
938
+ so developers can focus on building dynamic web applications without worrying
939
+ about abusing their RPC endpoint.
940
+
941
+ The resolver leverages query inspection before making any requests to the RPC,
942
+ in order to determine the most resource-conservative way to vend to your
943
+ application the requested response.
944
+
945
+ This results in four main benefits:
946
+
947
+ - Caching
948
+ - Request coalescing
949
+ - Minimized network payloads
950
+ - Batch loading
951
+
952
+ ## Caching
953
+
954
+ Caching is a fairly standard part of any good GraphQL library, and
955
+ `@solana/rpc-graphql` makes no exception.
956
+
957
+ If a query contains fetches for the same resource, the resolver can simply
958
+ fetch this information from the cache, ensuring no duplicate RPC requests
959
+ are ever made.
960
+
961
+ For example, if we were to query for a `MintAccount` and the `mintAuthority`
962
+ also happened to be the mint itself, the following query would ensure we only
963
+ fetch this account once.
964
+
965
+ ```graphql
966
+ query {
967
+ account(address: "J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ") {
968
+ lamports
969
+ data(encoding: BASE_64)
970
+ ... on MintAccount {
971
+ mintAuthority {
972
+ lamports
973
+ data(encoding: BASE_64)
974
+ }
975
+ }
976
+ }
977
+ }
978
+ ```
979
+
980
+ ## Request Coalescing
981
+
982
+ Sometimes more than one request can be coalesced into the same request, again
983
+ saving on network round-trips.
984
+
985
+ In the example below, we're making two queries for the same account, but
986
+ different fields.
987
+
988
+ ```graphql
989
+ query {
990
+ account(address: "J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ") {
991
+ lamports
992
+ space
993
+ }
994
+ account(address: "J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ") {
995
+ ... on NonceAccount {
996
+ authority {
997
+ address
998
+ }
999
+ blockhash
1000
+ feeCalculator {
1001
+ lamportsPerSignature
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ ```
1007
+
1008
+ Rather than requesting this account twice, the resolver will combine these
1009
+ two queries into the same RPC request, and then split the response out to the
1010
+ corresponding query results.
1011
+
1012
+ ## Minimized Network Payloads
1013
+
1014
+ When it comes to retrieving data from an RPC endpoint, fetching more
1015
+ information than you need can be a significant waste of network resources, and
1016
+ even impact application performance.
1017
+
1018
+ The RPC-GraphQL resolver takes steps to minimize this network overhead based on
1019
+ the contents of the query provided.
1020
+
1021
+ For example, in the following `account` query, we're going to request multiple
1022
+ responses for `base64` encoded data on the same account.
1023
+
1024
+ ```graphql
1025
+ query {
1026
+ account(address: "J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ") {
1027
+ firstEightBytes: data(encoding: BASE_64, dataSlice: { length: 8, offset: 0 })
1028
+ nextEightBytes: data(encoding: BASE_64, dataSlice: { length: 8, offset: 8 })
1029
+ anotherEightBytes: data(encoding: BASE_64, dataSlice: { length: 8, offset: 16 })
1030
+ }
1031
+ }
1032
+ ```
1033
+
1034
+ To gather this information, a developer may elect for one of two solutions:
1035
+
1036
+ 1. Call the RPC three times with each data slice. This will result in `3n`
1037
+ requests where `n` is the number of times your application may invoke this
1038
+ query.
1039
+ 2. Call the RPC once for the data, convert from `base64` to raw bytes, slice
1040
+ the raw bytes, then encode each subset back to `base64`. This requires a lot
1041
+ of overhead on application development.
1042
+
1043
+ RPC-GraphQL will perform solution two for you automatically, choosing to save
1044
+ on network calls and bytes over the wire in favor of slicing the returned data
1045
+ locally.
1046
+
1047
+ In fact, the resolver will minimize bytes over the wire by only requesting the
1048
+ specific slice of the data that encompasses all requested data slices. In the
1049
+ above example, we've requested three ranges of data:
1050
+
1051
+ - `0` - `8`
1052
+ - `8` - `16`
1053
+ - `16` - `24`
1054
+
1055
+ If this account has a massive amount of data, fetching more than the query asks
1056
+ for would be wasteful. The resolver will only fetch `0` - `24` and slice the
1057
+ response to serve the requested query.
1058
+
1059
+ ## Batch Loading
1060
+
1061
+ In some cases, the Solana JSON RPC offers batch loading for certain data types.
1062
+ One such example is the RPC methods `getAccountInfo` and `getMultipleAccounts`.
1063
+
1064
+ As one might predict, whenever multiple accounts are requested with parameters
1065
+ that can be coalesced, one single call to `getMultipleAccounts` can be made.
1066
+
1067
+ In the example above from [Request Coalescing](#request-coalescing), let's
1068
+ simply change the query to request two different accounts.
1069
+
1070
+ ```graphql
1071
+ query {
1072
+ account(address: "J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ") {
1073
+ lamports
1074
+ space
1075
+ }
1076
+ account(address: "EVW3CoyogapBfQxBFFEKGMM1bn3JyoFiqkAJdw3FHX1b") {
1077
+ ... on NonceAccount {
1078
+ authority {
1079
+ address
1080
+ }
1081
+ blockhash
1082
+ feeCalculator {
1083
+ lamportsPerSignature
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ ```
1089
+
1090
+ Now the resolver would recognize the distinction between the two accounts, but
1091
+ it would still see the ability to coalesce request parameters. As a result,
1092
+ RPC-GraphQL would make one call to `getMultipleAccounts` as follows.
1093
+
1094
+ ```ts
1095
+ rpc.getMultipleAccounts([
1096
+ 'J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ',
1097
+ 'EVW3CoyogapBfQxBFFEKGMM1bn3JyoFiqkAJdw3FHX1b',
1098
+ ]);
1099
+ ```
1100
+
1101
+ This batch loading can work in conjunction with the other forms of
1102
+ optimization as well, such as minimized network payloads.
1103
+
1104
+ ```graphql
1105
+ query {
1106
+ account(address: "J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ") {
1107
+ data(encoding: BASE_64, dataSlice: { length: 32, offset: 0 })
1108
+ }
1109
+ account(address: "EVW3CoyogapBfQxBFFEKGMM1bn3JyoFiqkAJdw3FHX1b") {
1110
+ authorityData: data(encoding: BASE_64, dataSlice: { length: 32, offset: 0 })
1111
+ u64Data: data(encoding: BASE_64, dataSlice: { length: 8, offset: 32 })
1112
+ }
1113
+ }
1114
+ ```
1115
+
1116
+ ```ts
1117
+ rpc.getMultipleAccounts(
1118
+ ['J7iup799j5BVjKXACZycYef7WQ4x1wfzhUsc5v357yWQ', 'EVW3CoyogapBfQxBFFEKGMM1bn3JyoFiqkAJdw3FHX1b'],
1119
+ {
1120
+ encoding: 'base64',
1121
+ dataSlice: { length: 40, offset: 0 },
1122
+ },
1123
+ );
1124
+ ```
1125
+
1126
+ In this case the resolver would ensure the proper data slices are dealt out
1127
+ from the single `getMultipleAccounts` response.