import QuickLRU from 'quick-lru' import { keccak256, stringify } from 'viem' type AsyncFunction = (...args: T) => Promise // Type definitions for the cache. type CacheOptions> = { name?: string maxCacheSize?: number ttl: number key?: (params: Parameters) => any } function calcCacheKey(args: any[], epoch: number) { const json = stringify(args) const r = keccak256(`0x${json}@${epoch}`) return r } const identity = (args: any) => args export const cacheByLRU = >(fn: T, { ttl, key, maxCacheSize }: CacheOptions) => { const cache = new QuickLRU>({ maxAge: ttl, maxSize: maxCacheSize || 1000, }) const keyFunction = key || identity let startTime = 0 return async (...args: Parameters): Promise> => { // Start Time if (!startTime) { startTime = Date.now() } const epoch = (Date.now() - startTime) / ttl const halfTTS = epoch % 1 > 0.5 const epochId = Math.floor(epoch) // Setup next epoch cache if halfTTS passed if (halfTTS) { const nextKey = calcCacheKey(keyFunction(args), epochId + 1) if (!cache.has(nextKey)) { const nextPromise = fn(...args) cache.set(nextKey, nextPromise) } } const cacheKey = calcCacheKey(keyFunction(args), epochId) // logger(cacheKey, `exists=${cache.has(cacheKey)}`) if (cache.has(cacheKey)) { return cache.get(cacheKey) } const promise = fn(...args) cache.set(cacheKey, promise) if (epochId > 0) { const prevKey = calcCacheKey(keyFunction(args), epochId - 1) if (cache.has(prevKey)) { return cache.get(prevKey) } } try { return await promise } catch (error) { // logger('error', cacheKey, error) cache.delete(cacheKey) throw error } } }