Skip to content

[SDK] switch to LRU caches #2215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clever-shoes-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@thirdweb-dev/sdk": patch
---

Switch to LRU caches for contract metadata
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any> = {};
type DeployAndPublishMetadata = {
compilerMetadata: PreDeployMetadataFetched;
extendedMetadata: FullPublishMetadata | undefined;
};

const deployMetadataCache =
/* @__PURE__ */ createLruCache<DeployAndPublishMetadata>(20);

/**
* @internal
Expand All @@ -12,8 +23,9 @@ export async function fetchAndCacheDeployMetadata(
publishMetadataUri: string,
storage: ThirdwebStorage,
): Promise<DeployMetadata> {
if (deployMetadataCache[publishMetadataUri]) {
return deployMetadataCache[publishMetadataUri];
const cached = deployMetadataCache.get(publishMetadataUri);
if (cached) {
return cached;
}
const compilerMetadata = await fetchPreDeployMetadata(
publishMetadataUri,
Expand All @@ -32,6 +44,6 @@ export async function fetchAndCacheDeployMetadata(
compilerMetadata,
extendedMetadata,
};
deployMetadataCache[publishMetadataUri] = data;
deployMetadataCache.put(publishMetadataUri, data);
return data;
}
14 changes: 8 additions & 6 deletions packages/sdk/src/evm/common/metadata-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, PublishedMetadata> = {};
const metadataCache = /* @__PURE__ */ createLruCache<PublishedMetadata>(20);
let multichainRegistry: Contract | undefined = undefined;

function getCacheKey(address: string, chainId: number) {
Expand All @@ -29,7 +30,7 @@ function putInCache(
chainId: number,
metadata: PublishedMetadata,
) {
metadataCache[getCacheKey(address, chainId)] = metadata;
metadataCache.put(getCacheKey(address, chainId), metadata);
}

/**
Expand All @@ -39,7 +40,7 @@ export function getContractMetadataFromCache(
address: string,
chainId: number,
): PublishedMetadata | undefined {
return metadataCache[getCacheKey(address, chainId)];
return metadataCache.get(getCacheKey(address, chainId));
}

/**
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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",
Expand Down
39 changes: 39 additions & 0 deletions packages/sdk/src/evm/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,42 @@ export function unique<T>(a: T[], fn: (a: T, b: T) => boolean): T[] {
}
return a;
}

/**
* @internal
*/
export function createLruCache<T>(
maxEntries: number,
store: Map<string, T> = new Map<string, T>(),
) {
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,
};
}
6 changes: 4 additions & 2 deletions packages/sdk/src/evm/functions/utils/cache.ts
Original file line number Diff line number Diff line change
@@ -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<string, ValidContractInstance>();
const CONTRACT_CACHE =
/* @__PURE__ */ createLruCache<ValidContractInstance>(10);

function getContractCacheKey(address: string, chainId: number) {
return `${address}-${chainId}`;
Expand Down Expand Up @@ -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 {
Expand Down