@solana/rpc-graphql 2.0.0-experimental.b153bb7 → 2.0.0-experimental.b419cd0

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.
@@ -1,4 +1,6 @@
1
1
  import { graphql, Kind } from 'graphql';
2
+ import DataLoader from 'dataloader';
3
+ import fastStableStringify from 'fast-stable-stringify';
2
4
  import { makeExecutableSchema } from '@graphql-tools/schema';
3
5
 
4
6
  // src/rpc.ts
@@ -38,8 +40,8 @@ function createGraphQLCache() {
38
40
  } ;
39
41
  }
40
42
 
41
- // src/loaders/account.ts
42
- function onlyAddressRequested(info) {
43
+ // src/loaders/common/resolve-info.ts
44
+ function onlyPresentFieldRequested(fieldName, info) {
43
45
  if (info && info.fieldNodes[0].selectionSet) {
44
46
  const selectionSet = info.fieldNodes[0].selectionSet;
45
47
  const requestedFields = selectionSet.selections.map((field) => {
@@ -48,12 +50,24 @@ function onlyAddressRequested(info) {
48
50
  }
49
51
  return null;
50
52
  });
51
- if (requestedFields && requestedFields.length === 1 && requestedFields[0] === "address") {
53
+ if (requestedFields && requestedFields.length === 1 && requestedFields[0] === fieldName) {
52
54
  return true;
53
55
  }
54
56
  }
55
57
  return false;
56
58
  }
59
+
60
+ // src/loaders/account.ts
61
+ function normalizeArgs(args) {
62
+ const { address, commitment, dataSlice, encoding, minContextSlot } = args;
63
+ return {
64
+ address,
65
+ commitment: commitment ?? "confirmed",
66
+ dataSlice,
67
+ encoding: encoding ?? "jsonParsed",
68
+ minContextSlot
69
+ };
70
+ }
57
71
  function refineJsonParsedAccountData(jsonParsedAccountData) {
58
72
  const meta = {
59
73
  program: jsonParsedAccountData.program,
@@ -79,86 +93,44 @@ function processQueryResponse({ address, account, encoding }) {
79
93
  data: refinedData
80
94
  };
81
95
  }
82
- async function loadAccount({ address, encoding = "jsonParsed", ...config }, cache, rpc, info) {
83
- if (onlyAddressRequested(info)) {
84
- return { address };
85
- }
86
- const requestConfig = { encoding, ...config };
87
- const cached = cache.get(address, requestConfig);
88
- if (cached !== null) {
89
- return cached;
90
- }
91
- const account = await rpc.getAccountInfo(address, requestConfig).send().then((res) => res.value).catch((e) => {
96
+ async function loadAccount(rpc, { address, ...config }) {
97
+ const { encoding } = config;
98
+ const account = await rpc.getAccountInfo(address, config).send().then((res) => res.value).catch((e) => {
92
99
  throw e;
93
100
  });
94
101
  if (account === null) {
95
102
  return { address };
96
103
  }
97
- const queryResponse = processQueryResponse({ account, address, encoding });
98
- cache.insert(address, requestConfig, queryResponse);
99
- return queryResponse;
104
+ return processQueryResponse({ account, address, encoding });
100
105
  }
101
-
102
- // src/loaders/block.ts
103
- async function loadBlock({ slot, encoding = "jsonParsed", ...config }, cache, rpc, _info) {
104
- const requestConfig = {
105
- encoding,
106
- ...config,
107
- // Always use 0 to avoid silly errors
108
- maxSupportedTransactionVersion: 0
106
+ function createAccountBatchLoadFn(rpc) {
107
+ const resolveAccountUsingRpc = loadAccount.bind(null, rpc);
108
+ return async (accountQueryArgs) => {
109
+ return await Promise.all(accountQueryArgs.map(async (args) => await resolveAccountUsingRpc(args)));
109
110
  };
110
- const cached = cache.get(slot, config);
111
- if (cached !== null) {
112
- return cached;
113
- }
114
- const block = await rpc.getBlock(slot, requestConfig).send();
115
- if (block === null) {
116
- return null;
117
- }
118
- cache.insert(slot, config, block);
119
- return block;
120
111
  }
121
-
122
- // src/loaders/program-accounts.ts
123
- function processQueryResponse2({ encoding, programAccounts }) {
124
- return programAccounts.map((programAccount) => {
125
- const [refinedData, responseEncoding] = Array.isArray(programAccount.account.data) ? encoding === "jsonParsed" ? [programAccount.account.data[0], "base64"] : [programAccount.account.data[0], encoding] : [refineJsonParsedAccountData(programAccount.account.data), "jsonParsed"];
126
- const pubkey = programAccount.pubkey;
127
- const responseBase = {
128
- ...programAccount.account,
129
- address: pubkey,
130
- encoding: responseEncoding
131
- };
132
- return typeof refinedData === "object" && "meta" in refinedData ? {
133
- ...responseBase,
134
- data: refinedData.data,
135
- meta: refinedData.meta
136
- } : {
137
- ...responseBase,
138
- data: refinedData
139
- };
140
- });
141
- }
142
- async function loadProgramAccounts({ programAddress, encoding = "jsonParsed", ...config }, cache, rpc, _info) {
143
- const requestConfig = { encoding, ...config };
144
- const cached = cache.get(programAddress, requestConfig);
145
- if (cached !== null) {
146
- return cached;
147
- }
148
- const programAccounts = await rpc.getProgramAccounts(programAddress, requestConfig).send().then((res) => {
149
- if ("value" in res) {
150
- return res.value;
112
+ function createAccountLoader(rpc) {
113
+ const loader = new DataLoader(createAccountBatchLoadFn(rpc), { cacheKeyFn: fastStableStringify });
114
+ return {
115
+ load: async (args, info) => {
116
+ if (onlyPresentFieldRequested("address", info)) {
117
+ return { address: args.address };
118
+ }
119
+ return loader.load(normalizeArgs(args));
151
120
  }
152
- return res;
153
- }).catch((e) => {
154
- throw e;
155
- });
156
- const queryResponse = processQueryResponse2({ encoding, programAccounts });
157
- cache.insert(programAddress, requestConfig, queryResponse);
158
- return queryResponse;
121
+ };
159
122
  }
160
123
 
161
124
  // src/loaders/transaction.ts
125
+ function normalizeArgs2(args) {
126
+ const { commitment, encoding } = args;
127
+ return {
128
+ commitment: commitment ?? "confirmed",
129
+ encoding: encoding ?? "jsonParsed",
130
+ // Always use 0 to avoid silly errors
131
+ maxSupportedTransactionVersion: 0
132
+ };
133
+ }
162
134
  function refineJsonParsedInstructionData(jsonParsedInstructionData) {
163
135
  if ("parsed" in jsonParsedInstructionData) {
164
136
  const meta = {
@@ -196,36 +168,36 @@ function refineJsonParsedTransactionMeta(jsonParsedTransactionMeta) {
196
168
  innerInstructions: refinedInnerInstructions
197
169
  };
198
170
  }
199
- function processQueryResponse3({ encoding, transaction }) {
200
- const [transactionData, transactionMeta, responseEncoding] = Array.isArray(transaction.transaction) ? [transaction.transaction[0], transaction.meta, encoding] : [
201
- refineJsonParsedTransactionData(transaction.transaction),
202
- refineJsonParsedTransactionMeta(transaction.meta),
203
- encoding
204
- ];
171
+ function refineJsonParsedTransaction({ encoding, transaction }) {
172
+ const [transactionData, transactionMeta] = Array.isArray(transaction.transaction) ? [transaction.transaction[0], transaction.meta] : [refineJsonParsedTransactionData(transaction.transaction), refineJsonParsedTransactionMeta(transaction.meta)];
205
173
  return {
206
- ...transaction,
207
174
  data: transactionData,
208
- encoding: responseEncoding,
209
- meta: transactionMeta
210
- };
211
- }
212
- async function loadTransaction({ signature, encoding = "jsonParsed", ...config }, cache, rpc, _info) {
213
- const requestConfig = {
214
175
  encoding,
215
- ...config,
216
- // Always use 0 to avoid silly errors
217
- maxSupportedTransactionVersion: 0
176
+ meta: transactionMeta,
177
+ slot: transaction.slot,
178
+ version: transaction.version
218
179
  };
180
+ }
181
+ function processQueryResponse2({ encoding, transaction }) {
182
+ return refineJsonParsedTransaction({ encoding, transaction });
183
+ }
184
+ async function loadTransaction({ signature, ...config }, cache, rpc, _info) {
185
+ const requestConfig = normalizeArgs2(config);
186
+ const { encoding } = requestConfig;
219
187
  const cached = cache.get(signature, requestConfig);
220
188
  if (cached !== null) {
221
189
  return cached;
222
190
  }
223
- let transaction = await rpc.getTransaction(signature, requestConfig).send();
191
+ let transaction = await rpc.getTransaction(signature, requestConfig).send().catch((e) => {
192
+ throw e;
193
+ });
224
194
  if (transaction === null) {
225
195
  return null;
226
196
  }
227
197
  if (encoding !== "jsonParsed") {
228
- const transactionJsonParsed = await rpc.getTransaction(signature, requestConfig).send();
198
+ const transactionJsonParsed = await rpc.getTransaction(signature, requestConfig).send().catch((e) => {
199
+ throw e;
200
+ });
229
201
  if (transactionJsonParsed === null) {
230
202
  return null;
231
203
  }
@@ -235,22 +207,140 @@ async function loadTransaction({ signature, encoding = "jsonParsed", ...config }
235
207
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
208
  };
237
209
  }
238
- const queryResponse = processQueryResponse3({ encoding, transaction });
210
+ const queryResponse = processQueryResponse2({ encoding, transaction });
239
211
  cache.insert(signature, requestConfig, queryResponse);
240
212
  return queryResponse;
241
213
  }
242
214
 
215
+ // src/loaders/block.ts
216
+ function normalizeArgs3(args) {
217
+ const { commitment, encoding, slot, transactionDetails } = args;
218
+ return {
219
+ commitment: commitment ?? "confirmed",
220
+ encoding: encoding ?? "jsonParsed",
221
+ // Always use 0 to avoid silly errors
222
+ maxSupportedTransactionVersion: 0,
223
+ slot,
224
+ transactionDetails: transactionDetails ?? "full"
225
+ };
226
+ }
227
+ function refineJsonParsedTransactionForAccounts({ transaction }) {
228
+ return {
229
+ data: transaction.transaction,
230
+ meta: transaction.meta,
231
+ version: transaction.version
232
+ };
233
+ }
234
+ function processQueryResponse3({
235
+ encoding,
236
+ block,
237
+ transactionDetails
238
+ }) {
239
+ if (typeof block === "object" && "transactions" in block) {
240
+ const refinedBlock = {
241
+ ...block,
242
+ transactions: block.transactions.map((transaction) => {
243
+ if (transactionDetails === "accounts") {
244
+ return refineJsonParsedTransactionForAccounts({ transaction });
245
+ } else {
246
+ return refineJsonParsedTransaction({ encoding, transaction });
247
+ }
248
+ })
249
+ };
250
+ block = refinedBlock;
251
+ }
252
+ return {
253
+ ...block,
254
+ encoding,
255
+ transactionDetails
256
+ };
257
+ }
258
+ async function loadBlock(rpc, { slot, ...config }) {
259
+ const { encoding, transactionDetails } = config;
260
+ const block = await rpc.getBlock(slot, config).send().catch((e) => {
261
+ throw e;
262
+ });
263
+ if (block === null) {
264
+ return { slot };
265
+ }
266
+ const queryResponse = processQueryResponse3({ block, encoding, transactionDetails });
267
+ return queryResponse;
268
+ }
269
+ function createBlockBatchLoadFn(rpc) {
270
+ const resolveBlockUsingRpc = loadBlock.bind(null, rpc);
271
+ return async (blockQueryArgs) => {
272
+ return await Promise.all(blockQueryArgs.map(async (args) => await resolveBlockUsingRpc(args)));
273
+ };
274
+ }
275
+ function createBlockLoader(rpc) {
276
+ const loader = new DataLoader(createBlockBatchLoadFn(rpc), { cacheKeyFn: fastStableStringify });
277
+ return {
278
+ load: async (args, info) => {
279
+ if (onlyPresentFieldRequested("slot", info)) {
280
+ return { slot: args.slot };
281
+ }
282
+ return loader.load(normalizeArgs3(args));
283
+ }
284
+ };
285
+ }
286
+
287
+ // src/loaders/program-accounts.ts
288
+ function normalizeArgs4(args) {
289
+ const { commitment, dataSlice, encoding, filters, minContextSlot } = args;
290
+ return {
291
+ commitment: commitment ?? "confirmed",
292
+ dataSlice,
293
+ encoding: encoding ?? "jsonParsed",
294
+ filters,
295
+ minContextSlot
296
+ };
297
+ }
298
+ function processQueryResponse4({ encoding, programAccounts }) {
299
+ return programAccounts.map((programAccount) => {
300
+ const [refinedData, responseEncoding] = Array.isArray(programAccount.account.data) ? encoding === "jsonParsed" ? [programAccount.account.data[0], "base64"] : [programAccount.account.data[0], encoding] : [refineJsonParsedAccountData(programAccount.account.data), "jsonParsed"];
301
+ const pubkey = programAccount.pubkey;
302
+ const responseBase = {
303
+ ...programAccount.account,
304
+ address: pubkey,
305
+ encoding: responseEncoding
306
+ };
307
+ return typeof refinedData === "object" && "meta" in refinedData ? {
308
+ ...responseBase,
309
+ data: refinedData.data,
310
+ meta: refinedData.meta
311
+ } : {
312
+ ...responseBase,
313
+ data: refinedData
314
+ };
315
+ });
316
+ }
317
+ async function loadProgramAccounts({ programAddress, ...config }, cache, rpc, _info) {
318
+ const requestConfig = normalizeArgs4(config);
319
+ const { encoding } = requestConfig;
320
+ const cached = cache.get(programAddress, requestConfig);
321
+ if (cached !== null) {
322
+ return cached;
323
+ }
324
+ const programAccounts = await rpc.getProgramAccounts(programAddress, requestConfig).send().then((res) => {
325
+ if ("value" in res) {
326
+ return res.value;
327
+ }
328
+ return res;
329
+ }).catch((e) => {
330
+ throw e;
331
+ });
332
+ const queryResponse = processQueryResponse4({ encoding, programAccounts });
333
+ cache.insert(programAddress, requestConfig, queryResponse);
334
+ return queryResponse;
335
+ }
336
+
243
337
  // src/context.ts
244
338
  function createSolanaGraphQLContext(rpc) {
245
339
  const cache = createGraphQLCache();
246
340
  return {
341
+ accountLoader: createAccountLoader(rpc),
342
+ blockLoader: createBlockLoader(rpc),
247
343
  cache,
248
- loadAccount(args, info) {
249
- return loadAccount(args, this.cache, this.rpc, info);
250
- },
251
- loadBlock(args, info) {
252
- return loadBlock(args, this.cache, this.rpc);
253
- },
254
344
  loadProgramAccounts(args, info) {
255
345
  return loadProgramAccounts(args, this.cache, this.rpc);
256
346
  },
@@ -263,7 +353,7 @@ function createSolanaGraphQLContext(rpc) {
263
353
 
264
354
  // src/resolvers/account.ts
265
355
  var resolveAccount = (fieldName) => {
266
- return (parent, args, context, info) => parent[fieldName] === null ? null : context.loadAccount({ ...args, address: parent[fieldName] }, info);
356
+ return (parent, args, context, info) => parent[fieldName] === null ? null : context.accountLoader.load({ ...args, address: parent[fieldName] }, info);
267
357
  };
268
358
 
269
359
  // src/schema/account.ts
@@ -604,59 +694,79 @@ var blockTypeDefs = (
604
694
 
605
695
  # Block interface
606
696
  interface Block {
607
- blockHeight: BigInt
608
- blockTime: BigInt
609
697
  blockhash: String
698
+ blockHeight: BigInt
699
+ blockTime: Int
610
700
  parentSlot: BigInt
611
701
  previousBlockhash: String
612
702
  rewards: [Reward]
703
+ transactionDetails: String
613
704
  }
614
705
 
615
706
  # A block with account transaction details
616
- type BlockWithAccounts {
617
- blockHeight: BigInt
618
- blockTime: BigInt
707
+ type BlockWithAccounts implements Block {
619
708
  blockhash: String
709
+ blockHeight: BigInt
710
+ blockTime: Int
620
711
  parentSlot: BigInt
621
712
  previousBlockhash: String
622
713
  rewards: [Reward]
623
714
  transactions: [BlockTransactionAccounts]
715
+ transactionDetails: String
624
716
  }
625
717
 
626
718
  # A block with full transaction details
627
- type BlockFull {
628
- blockHeight: BigInt
629
- blockTime: BigInt
719
+ type BlockWithFull implements Block {
630
720
  blockhash: String
721
+ blockHeight: BigInt
722
+ blockTime: Int
631
723
  parentSlot: BigInt
632
724
  previousBlockhash: String
633
725
  rewards: [Reward]
634
726
  transactions: [Transaction]
727
+ transactionDetails: String
635
728
  }
636
729
 
637
730
  # A block with none transaction details
638
- type BlockWithNone {
639
- blockHeight: BigInt
640
- blockTime: BigInt
731
+ type BlockWithNone implements Block {
641
732
  blockhash: String
733
+ blockHeight: BigInt
734
+ blockTime: Int
642
735
  parentSlot: BigInt
643
736
  previousBlockhash: String
644
737
  rewards: [Reward]
738
+ transactionDetails: String
645
739
  }
646
740
 
647
741
  # A block with signature transaction details
648
- type BlockWithSignatures {
649
- blockHeight: BigInt
650
- blockTime: BigInt
742
+ type BlockWithSignatures implements Block {
651
743
  blockhash: String
744
+ blockHeight: BigInt
745
+ blockTime: Int
652
746
  parentSlot: BigInt
653
747
  previousBlockhash: String
654
748
  rewards: [Reward]
655
749
  signatures: [String]
750
+ transactionDetails: String
656
751
  }
657
752
  `
658
753
  );
659
- var blockResolvers = {};
754
+ var blockResolvers = {
755
+ Block: {
756
+ __resolveType(block) {
757
+ switch (block.transactionDetails) {
758
+ case "accounts":
759
+ return "BlockWithAccounts";
760
+ case "none":
761
+ return "BlockWithNone";
762
+ case "signatures":
763
+ return "BlockWithSignatures";
764
+ default:
765
+ return "BlockWithFull";
766
+ }
767
+ }
768
+ }
769
+ };
660
770
 
661
771
  // src/schema/common/inputs.ts
662
772
  var inputTypeDefs = (
@@ -2877,10 +2987,10 @@ var schemaTypeDefs = (
2877
2987
  var schemaResolvers = {
2878
2988
  Query: {
2879
2989
  account(_, args, context, info) {
2880
- return context.loadAccount(args, info);
2990
+ return context.accountLoader.load(args, info);
2881
2991
  },
2882
2992
  block(_, args, context, info) {
2883
- return context.loadBlock(args, info);
2993
+ return context.blockLoader.load(args, info);
2884
2994
  },
2885
2995
  programAccounts(_, args, context, info) {
2886
2996
  return context.loadProgramAccounts(args, info);