pyth.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { unstable_cache } from "next/cache";
  2. import { cache } from 'react';
  3. import superjson from "superjson";
  4. import { z } from "zod";
  5. // Your imports
  6. import { Cluster, clients, priceFeedsSchema } from "../services/pyth";
  7. const getDataCached = cache(async (cluster: Cluster) => {
  8. return clients[cluster].getData();
  9. });
  10. const MAX_CACHE_SIZE_STRING = 2 * 1024 * 1024;
  11. const getPublishersForFeed = unstable_cache(async (cluster: Cluster, chunk?: number) => {
  12. const data = await getDataCached(cluster);
  13. const result: Record<string, string[]> = {};
  14. for (const key of data.productPrice.keys()) {
  15. const price = data.productPrice.get(key);
  16. result[key] = price?.priceComponents.map(({ publisher }) => publisher.toBase58()) ?? [];
  17. }
  18. const stringifiedResult = superjson.stringify(result);
  19. const chunksNumber = Math.ceil(stringifiedResult.length / MAX_CACHE_SIZE_STRING);
  20. const chunks = [];
  21. for(let i = 0; i < chunksNumber; i++) {
  22. chunks.push(stringifiedResult.slice(i * MAX_CACHE_SIZE_STRING, (i + 1) * MAX_CACHE_SIZE_STRING));
  23. }
  24. return {
  25. chunk: chunks[chunk ?? 0],
  26. chunksNumber,
  27. };
  28. }, [], { revalidate: false });
  29. const _getFeeds = unstable_cache(async (cluster: Cluster, chunk?: number) => {
  30. // eslint-disable-next-line no-console
  31. console.log('getFeeds', cluster, chunk);
  32. const data = await getDataCached(cluster);
  33. const parsedData = priceFeedsSchema.parse(
  34. data.symbols
  35. .filter(
  36. (symbol) =>
  37. data.productFromSymbol.get(symbol)?.display_symbol !== undefined
  38. )
  39. .map((symbol) => ({
  40. symbol,
  41. product: data.productFromSymbol.get(symbol),
  42. price: {
  43. ...data.productPrice.get(symbol),
  44. priceComponents:
  45. data.productPrice
  46. .get(symbol)
  47. ?.priceComponents.map(({ publisher }) => ({
  48. publisher: publisher.toBase58(),
  49. })) ?? [],
  50. },
  51. }))
  52. )
  53. const result = superjson.stringify(
  54. parsedData
  55. );
  56. const chunksNumber = Math.ceil(result.length / MAX_CACHE_SIZE_STRING);
  57. const chunks = [];
  58. for(let i = 0; i < chunksNumber; i++) {
  59. chunks.push(result.slice(i * MAX_CACHE_SIZE_STRING, (i + 1) * MAX_CACHE_SIZE_STRING));
  60. }
  61. return {
  62. chunk: chunks[chunk ?? 0],
  63. chunksNumber,
  64. };
  65. }, [], { revalidate: false });
  66. export const getFeedsCached = async (cluster: Cluster) => {
  67. const start = performance.now();
  68. const { chunk, chunksNumber } = await _getFeeds(cluster); // uses cache
  69. const rawResults = await Promise.all(Array.from({ length: chunksNumber-1 }, (_, i) => _getFeeds(cluster, i+1)));
  70. const rawJson = [chunk, ...rawResults.map(({ chunk }) => chunk)].join('');
  71. const data = superjson.parse<z.infer<typeof priceFeedsSchema>>(rawJson);
  72. const end = performance.now();
  73. // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
  74. console.log(`getFeedsCached: ${end - start}ms`);
  75. return data;
  76. };
  77. export const getPublishersForFeedCached = async (cluster: Cluster, symbol: string) => {
  78. const start = performance.now();
  79. const { chunk, chunksNumber } = await getPublishersForFeed(cluster); // uses cache
  80. const rawResults = await Promise.all(Array.from({ length: chunksNumber-1 }, (_, i) => getPublishersForFeed(cluster, i+1)));
  81. const rawJson = [chunk, ...rawResults.map(({ chunk }) => chunk)].join('');
  82. const data = superjson.parse<Record<string, string[]>>(rawJson);
  83. const end = performance.now();
  84. // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
  85. console.log(`getPublishersForFeedCached: ${end - start}ms`);
  86. return data[symbol];
  87. };
  88. export const getFeedsForPublisherCached = async (
  89. cluster: Cluster,
  90. publisher: string
  91. ) => {
  92. const start = performance.now();
  93. const data = await getFeedsCached(cluster); // uses cache
  94. const end = performance.now();
  95. // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
  96. console.log(`getFeedsForPublisherCached: ${end - start}ms.`);
  97. return priceFeedsSchema.parse(
  98. data.filter(({ price }) =>
  99. price.priceComponents.some(
  100. (component) => component.publisher.toString() === publisher
  101. )
  102. )
  103. );
  104. };