diff --git a/.changeset/clever-shoes-help.md b/.changeset/clever-shoes-help.md new file mode 100644 index 00000000000..fee1609e12e --- /dev/null +++ b/.changeset/clever-shoes-help.md @@ -0,0 +1,5 @@ +--- +"@thirdweb-dev/sdk": patch +--- + +Switch to LRU caches for contract metadata diff --git a/packages/sdk/src/evm/common/any-evm-utils/fetchAndCacheDeployMetadata.ts b/packages/sdk/src/evm/common/any-evm-utils/fetchAndCacheDeployMetadata.ts index f3ef67a51fa..1a40aaa238a 100644 --- a/packages/sdk/src/evm/common/any-evm-utils/fetchAndCacheDeployMetadata.ts +++ b/packages/sdk/src/evm/common/any-evm-utils/fetchAndCacheDeployMetadata.ts @@ -2,8 +2,19 @@ import { ThirdwebStorage } from "@thirdweb-dev/storage"; import { fetchExtendedReleaseMetadata } from "../feature-detection/fetchExtendedReleaseMetadata"; import { fetchPreDeployMetadata } from "../feature-detection/fetchPreDeployMetadata"; import type { DeployMetadata } from "../../types/deploy/deploy-options"; +import { createLruCache } from "../utils"; +import { + FullPublishMetadata, + PreDeployMetadataFetched, +} from "../../schema/contracts/custom"; -const deployMetadataCache: Record = {}; +type DeployAndPublishMetadata = { + compilerMetadata: PreDeployMetadataFetched; + extendedMetadata: FullPublishMetadata | undefined; +}; + +const deployMetadataCache = + /* @__PURE__ */ createLruCache(20); /** * @internal @@ -12,8 +23,9 @@ export async function fetchAndCacheDeployMetadata( publishMetadataUri: string, storage: ThirdwebStorage, ): Promise { - if (deployMetadataCache[publishMetadataUri]) { - return deployMetadataCache[publishMetadataUri]; + const cached = deployMetadataCache.get(publishMetadataUri); + if (cached) { + return cached; } const compilerMetadata = await fetchPreDeployMetadata( publishMetadataUri, @@ -32,6 +44,6 @@ export async function fetchAndCacheDeployMetadata( compilerMetadata, extendedMetadata, }; - deployMetadataCache[publishMetadataUri] = data; + deployMetadataCache.put(publishMetadataUri, data); return data; } diff --git a/packages/sdk/src/evm/common/metadata-resolver.ts b/packages/sdk/src/evm/common/metadata-resolver.ts index 313fde44151..9efbca8b15f 100644 --- a/packages/sdk/src/evm/common/metadata-resolver.ts +++ b/packages/sdk/src/evm/common/metadata-resolver.ts @@ -13,11 +13,12 @@ import type { TWMultichainRegistryLogic } from "@thirdweb-dev/contracts-js"; import { constructAbiFromBytecode } from "./feature-detection/getAllDetectedFeatures"; import { SDKOptions } from "../schema/sdk-options"; import { Polygon } from "@thirdweb-dev/chains"; +import { createLruCache } from "./utils"; const CONTRACT_RESOLVER_BASE_URL = "https://contract.thirdweb.com/metadata"; // Internal static cache -const metadataCache: Record = {}; +const metadataCache = /* @__PURE__ */ createLruCache(20); let multichainRegistry: Contract | undefined = undefined; function getCacheKey(address: string, chainId: number) { @@ -29,7 +30,7 @@ function putInCache( chainId: number, metadata: PublishedMetadata, ) { - metadataCache[getCacheKey(address, chainId)] = metadata; + metadataCache.put(getCacheKey(address, chainId), metadata); } /** @@ -39,7 +40,7 @@ export function getContractMetadataFromCache( address: string, chainId: number, ): PublishedMetadata | undefined { - return metadataCache[getCacheKey(address, chainId)]; + return metadataCache.get(getCacheKey(address, chainId)); } /** @@ -95,6 +96,10 @@ export async function fetchContractMetadataFromAddress( if (!metadata.isPartialAbi) { putInCache(address, chainId, metadata); + } else { + console.warn( + `Contract metadata could only be partially resolved, some contract functions might be unavailable. Try importing the contract by visiting: https://thirdweb.com/${chainId}/${address}`, + ); } return metadata; } @@ -141,9 +146,6 @@ export async function fetchContractMetadataFromBytecode( if (!metadata && bytecode) { const abi = constructAbiFromBytecode(bytecode); if (abi && abi.length > 0) { - console.warn( - `Contract metadata could only be partially resolved, some contract functions might be unavailable. Try importing the contract by visiting: https://thirdweb.com/${chainId}/${address}`, - ); // return partial ABI metadata = { name: "Unimported Contract", diff --git a/packages/sdk/src/evm/common/utils.ts b/packages/sdk/src/evm/common/utils.ts index 039a0a1f4a0..14d3fd80eff 100644 --- a/packages/sdk/src/evm/common/utils.ts +++ b/packages/sdk/src/evm/common/utils.ts @@ -28,3 +28,42 @@ export function unique(a: T[], fn: (a: T, b: T) => boolean): T[] { } return a; } + +/** + * @internal + */ +export function createLruCache( + maxEntries: number, + store: Map = new Map(), +) { + function put(key: string, value: T) { + if (store.size >= maxEntries) { + const keyToDelete = store.keys().next().value; + store.delete(keyToDelete); + } + store.set(key, value); + } + + function get(key: string): T | undefined { + const hasKey = store.has(key); + if (!hasKey) { + return undefined; + } + const entry = store.get(key) as T; + store.delete(key); + store.set(key, entry); + return entry; + } + + function has(key: string): boolean { + return store.has(key); + } + + return { + put, + get, + has, + maxEntries, + store, + }; +} diff --git a/packages/sdk/src/evm/functions/utils/cache.ts b/packages/sdk/src/evm/functions/utils/cache.ts index 55ed95f3f1f..a74c98454fb 100644 --- a/packages/sdk/src/evm/functions/utils/cache.ts +++ b/packages/sdk/src/evm/functions/utils/cache.ts @@ -1,8 +1,10 @@ +import { createLruCache } from "../../common/utils"; import { ValidContractInstance } from "../../contracts"; import { ThirdwebStorage } from "@thirdweb-dev/storage"; let STORAGE_CACHE = new ThirdwebStorage(); -const CONTRACT_CACHE = new Map(); +const CONTRACT_CACHE = + /* @__PURE__ */ createLruCache(10); function getContractCacheKey(address: string, chainId: number) { return `${address}-${chainId}`; @@ -31,7 +33,7 @@ export function cacheContract( chainId: number, ) { const cacheKey = getContractCacheKey(address, chainId); - CONTRACT_CACHE.set(cacheKey, contract); + CONTRACT_CACHE.put(cacheKey, contract); } export function getCachedStorage(storage?: ThirdwebStorage): ThirdwebStorage {