From 76e425bdf5d4c78a9f4e2b4198a6489724660e14 Mon Sep 17 00:00:00 2001 From: Sarju Hansaliya Date: Sun, 17 Dec 2023 11:39:53 +0530 Subject: [PATCH] fix: cache gql and rpc call responses --- src/utils/calls.ts | 41 +++++++++++++++++++++++++++++++---------- src/utils/gql.ts | 34 ++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/utils/calls.ts b/src/utils/calls.ts index 4147811..c545d5a 100644 --- a/src/utils/calls.ts +++ b/src/utils/calls.ts @@ -1,8 +1,8 @@ -import {Interface, Result} from '@ethersproject/abi'; -import {BigNumber} from '@ethersproject/bignumber'; -import {hexStripZeros, hexZeroPad} from '@ethersproject/bytes'; -import {ERC20_ABI, MINICHEF_ABI, REWARDER_VIA_MULTIPLIER_ABI} from '../constants'; -import {CloudflareWorkerKV, PoolInfo} from './interfaces'; +import { Interface } from '@ethersproject/abi'; +import { BigNumber } from '@ethersproject/bignumber'; +import { hexStripZeros, hexZeroPad } from '@ethersproject/bytes'; +import { ERC20_ABI, MINICHEF_ABI, REWARDER_VIA_MULTIPLIER_ABI } from '../constants'; +import { CloudflareWorkerKV, PoolInfo } from './interfaces'; export function normalizeAddress(address: string): string { return hexZeroPad(hexStripZeros(address), 20); @@ -136,6 +136,14 @@ export async function getBalance(rpc: string, erc20: string, address: string): P return BigNumber.from(await call(rpc, ERC20_ABI, erc20, 'balanceOf', [address])); } +interface CacheEntry { + timestamp: number; + data: any; +} + +const rpcCache: Record = {}; +const CACHE_EXPIRE_TIME = 5 * 60 * 1000; // 5 minutes + export async function call( rpc: string, abi: any[], @@ -144,10 +152,17 @@ export async function call( functionData: any[] = [], ) { const iface = new Interface(abi); + const cacheKey = `${rpc}|${toAddress}|${functionName}|${JSON.stringify(functionData)}`; + const currentTime = Date.now(); - const _ = await fetch(rpc, { + // Check if the cache contains a valid entry + if (rpcCache[cacheKey] && currentTime - rpcCache[cacheKey].timestamp < CACHE_EXPIRE_TIME) { + return rpcCache[cacheKey].data; + } + + const response = await fetch(rpc, { method: 'POST', - headers: {'Content-Type': 'application/json'}, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: 1, jsonrpc: '2.0', @@ -162,13 +177,19 @@ export async function call( }), }); - if (_.status !== 200) { - const message = `[${_.statusText}]: Error fetching ${toAddress}.${functionName}(...)`; + if (response.status !== 200) { + const message = `[${response.statusText}]: Error fetching ${toAddress}.${functionName}(...)`; console.error(message); throw new Error(message); } - const {result} = await _.json(); + const { result } = await response.json(); + + // Cache the new data with a timestamp + rpcCache[cacheKey] = { + timestamp: currentTime, + data: result, + }; return result; } diff --git a/src/utils/gql.ts b/src/utils/gql.ts index 8b7895a..2c44d20 100644 --- a/src/utils/gql.ts +++ b/src/utils/gql.ts @@ -17,7 +17,7 @@ export async function getTokenPriceUSD(url: string | undefined, address: string) export async function getTokenInfo( url: string | undefined, address: string, -): Promise<{decimals: string; derivedETH: string}> { +): Promise<{ decimals: string; derivedETH: string }> { const response = await request(QUERIES.TOKEN_INFO, url, { address: address.toLowerCase(), }); @@ -39,27 +39,49 @@ export async function getETHPrice(url: string | undefined): Promise { return response.bundle.ethPrice; } +interface CacheEntry { + timestamp: number; + data: any; +} + +const gqlCache: Record = {}; +const CACHE_EXPIRE_TIME = 5 * 60 * 1000; // 5 minutes + export async function request(query: string, url: string | undefined, variables = {}) { if (url === undefined) { throw new Error(`Missing subgraph url`); } - const _ = await fetch(url, { + const cacheKey = `${url}|${query}|${JSON.stringify(variables)}`; + const currentTime = Date.now(); + + // Check if the cache contains a valid entry + if (gqlCache[cacheKey] && currentTime - gqlCache[cacheKey].timestamp < CACHE_EXPIRE_TIME) { + return gqlCache[cacheKey].data; + } + + const response = await fetch(url, { method: 'POST', - headers: {'Content-Type': 'application/json'}, + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables, }), }); - if (_.status !== 200) { - const message = `[${_.statusText}]: Error querying ${query}`; + if (response.status !== 200) { + const message = `[${response.statusText}]: Error querying ${query}`; console.error(message); throw new Error(message); } - const {data} = await _.json(); + const { data } = await response.json(); + + // Cache the new data with a timestamp + gqlCache[cacheKey] = { + timestamp: currentTime, + data, + }; return data; }