Skip to content

Instantly share code, notes, and snippets.

@ZaK3939
Last active November 4, 2022 04:54
Show Gist options
  • Save ZaK3939/c0e3526a4e4707038a5d08eae1513191 to your computer and use it in GitHub Desktop.
Save ZaK3939/c0e3526a4e4707038a5d08eae1513191 to your computer and use it in GitHub Desktop.
import { targetEnv, isLocal } from "../env";
import { getCache, putCache } from "./ddbCache";
const TABLE_NAME = "cache-" + targetEnv;
const DEFAULT_TTL = 90000;
export type CachedContractCallOptions = {
tableName?: string;
ttl?: number;
toJson?: boolean;
}
function convertOutput(result: any, toJson?: boolean): string {
if (toJson) {
return JSON.stringify(result);
}
if ((result as any)._isBigNumber) {
return result.toString();
}
return result;
}
/**
* WARNING supported contract return types are number (BigNumber), string, struct
* other data types are not guaranteed to work
* set "opts.toJson" to true if smart contract return type is struct or a list of structs
* and this will convert output to json string
* @param contract
* @param functionName
* @param args
* @param opts
* @returns
*/
export async function cachedContractCall(contract: any, functionName: string, args: any[], opts?: CachedContractCallOptions): Promise<string> {
if (isLocal) {
return convertOutput(await contract[functionName](...args), opts?.toJson);
}
let networkName;
if (!contract.provider.uniqueNetwork) {
networkName = "unknown";
console.warn("unknown network");
}
else {
networkName = typeof(contract.provider.uniqueNetwork) == "string" ? contract.provider.uniqueNetwork : contract.provider.uniqueNetwork.name;
}
const cacheKey = `${contract.address}.${networkName}.${functionName}.${JSON.stringify(args)}`;
const tableName = opts?.tableName ?? TABLE_NAME;
const ttl = opts?.ttl ?? DEFAULT_TTL;
console.log("try get cache", tableName, cacheKey)
let result = await getCache(cacheKey, tableName);
if (result) {
console.log("hit cache for", cacheKey);
return result;
}
// if not found in cache, call contract directly
result = await contract[functionName](...args);
result = convertOutput(result, opts?.toJson);
await putCache(cacheKey, result, ttl, tableName);
return result;
}
import aws from "aws-sdk";
import { targetEnv, isLocal } from "../env";
const ddbClient = new aws.DynamoDB.DocumentClient({ region: "ap-northeast-1" });
const TABLE_NAME = "cache-" + targetEnv;
const PREFIX = "CACHE";
/**
* put a key-value pair to cache
* @param key
* @param value
* @param ttlMs
* @param tableNameOverride
* @returns success status
*/
export async function putCache(key: string, value: string, ttlMs: number, tableNameOverride?: string): Promise<boolean> {
if (isLocal) { return false; }
const hashKey = [PREFIX, key].join("-");
const expire = Date.now() + ttlMs;
try {
await ddbClient.put({
TableName: tableNameOverride || TABLE_NAME,
Item: {
HashKey: hashKey,
Value: value,
Expire: expire
}
}).promise();
return true;
}
catch (e) {
if (value.length > 2000) {
console.error(`ddbCache put error: ${JSON.stringify(e)}, key: ${key}, ttlMs: ${ttlMs}`);
}
else {
console.error(`ddbCache put error: ${JSON.stringify(e)}, key: ${key}, value: ${value}, ttlMs: ${ttlMs}`);
}
}
return false;
}
export async function getCache(key: string, tableNameOverride?: string): Promise<string | null> {
if (isLocal) { return null; }
const hashKey = [PREFIX, key].join("-");
let result: string | null = null;
try {
const data = await ddbClient.get({
TableName: tableNameOverride || TABLE_NAME,
Key: {
HashKey: hashKey
}
}).promise();
if (data?.Item?.Value && data?.Item?.Expire) {
if (parseInt(data.Item.Expire) > Date.now()) {
result = data.Item.Value;
}
}
}
catch (e) {
console.error(`ddbCache get error: ${JSON.stringify(e)}, key: ${key}`);
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment