Skip to content

Commit

Permalink
feat: base caching (#55)
Browse files Browse the repository at this point in the history
# 🤖 Linear

Closes ZKS-208 Closes ZKS-199

## Description

- Added caching of 60s default to `ecosystem` and `zkchain` endpoints
- Moved core contract addresses from hardcoded config to env variables

We decided to move forward with `NodeCache` since `cacheable` by nature
uses Promises so hit the cache, this causes some problems when trying to
override `res.json` hook on express since `res.json` is considered sync.

For next steps we will:
- keep using NodeCache as Highlevel api caching
- keep using `cacheable` for modules advanced caching 
- analyze with more time if we can effectively use `cacheable` for the
cacheMiddleware
  • Loading branch information
0xkenj1 authored Aug 23, 2024
1 parent 155aac9 commit 66359db
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 12 deletions.
9 changes: 7 additions & 2 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
PORT=3000 # Port to run the API server on
L1_RPC_URLS="" #CSV list of L2 RPC URLs
L2_RPC_URLS="" #CSV list of L1 RPC URLs

BRIDGE_HUB_ADDRESS=""
SHARED_BRIDGE_ADDRESS=""
STATE_MANAGER_ADDRESSES="" #CSV list of State managers addresses

L1_RPC_URLS="" #CSV list of L1 RPC URLs
L2_RPC_URLS="" #CSV list of L2 RPC URLs

COINGECKO_API_KEY='' # CoinGecko API key
COINGECKO_BASE_URL='' # CoinGecko API base URL for the API version you are using
Expand Down
3 changes: 2 additions & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
"test:cov": "vitest run --config vitest.config.ts --coverage"
},
"dependencies": {
"@zkchainhub/chain-providers": "workspace:*",
"@zkchainhub/metrics": "workspace:*",
"@zkchainhub/pricing": "workspace:*",
"@zkchainhub/chain-providers": "workspace:*",
"@zkchainhub/shared": "workspace:*",
"bignumber.js": "9.1.2",
"cache-manager": "5.7.6",
"cors": "2.8.5",
"dotenv": "16.4.5",
"express": "4.19.2",
"node-cache": "5.1.2",
"swagger-ui-express": "5.0.1",
"viem": "2.19.6",
"yaml": "2.5.0",
Expand Down
21 changes: 17 additions & 4 deletions apps/api/src/common/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dotenv from "dotenv";
import { Address } from "viem";
import { Address, isAddress } from "viem";
import { mainnet, zksync } from "viem/chains";
import { z } from "zod";

Expand All @@ -9,6 +9,16 @@ dotenv.config();

const logger = Logger.getInstance();

const addressArraySchema = z
.string()
.transform((str) => str.split(","))
.refine((addresses) => addresses.every((address) => isAddress(address)), {
message: "Must be a comma-separated list of valid Addresses",
});
const addressSchema = z.string().refine((address) => isAddress(address), {
message: "Must be a valid Address",
});

const urlArraySchema = z
.string()
.transform((str) => str.split(","))
Expand All @@ -18,6 +28,9 @@ const urlArraySchema = z

const validationSchema = z.object({
PORT: z.coerce.number().positive().default(3000),
BRIDGE_HUB_ADDRESS: addressSchema,
SHARED_BRIDGE_ADDRESS: addressSchema,
STATE_MANAGER_ADDRESSES: addressArraySchema,
L1_RPC_URLS: urlArraySchema,
L2_RPC_URLS: z
.union([z.literal(""), urlArraySchema])
Expand Down Expand Up @@ -54,9 +67,9 @@ export const config = {
chain: zksync,
}
: undefined,
bridgeHubAddress: "0x303a465B659cBB0ab36eE643eA362c509EEb5213" as Address,
sharedBridgeAddress: "0xD7f9f54194C633F36CCD5F3da84ad4a1c38cB2cB" as Address,
stateTransitionManagerAddresses: ["0xc2eE6b6af7d616f6e27ce7F4A451Aedc2b0F5f5C"] as Address[],
bridgeHubAddress: envData.BRIDGE_HUB_ADDRESS as Address,
sharedBridgeAddress: envData.SHARED_BRIDGE_ADDRESS as Address,
stateTransitionManagerAddresses: envData.STATE_MANAGER_ADDRESSES as Address[],
pricing: {
cacheOptions: {
ttl: envData.CACHE_TTL,
Expand Down
36 changes: 36 additions & 0 deletions apps/api/src/common/middleware/cache.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { NextFunction, Request, Response } from "express";
import NodeCache from "node-cache";

const DEFAULT_TTL = 60; // 1 minute
const cache = new NodeCache();

//FIXME: This is a temporary cache implementation. It is not recommended for production use.
// might be replaced with a more robust solution in the future.
/**
* A middleware that caches responses for a given time to live (TTL).
* @param args - The time to live (TTL) in seconds for the cached response.
* @returns A middleware function that caches responses for a given time to live (TTL).
*/
export function cacheMiddleware(args: { ttl: number } = { ttl: DEFAULT_TTL }) {
return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const key = req.originalUrl || req.url;
const cachedResponse = await cache.get(key);
if (cachedResponse) {
// Check if the cached response is a JSON object or plain text
res.json(cachedResponse);
} else {
// Store the original send and json functions
const originalJson = res.json.bind(res);
// Override the json function

res.json = (body): Response => {
// Cache the response body
cache.set(key, body, args.ttl);
// Call the original json function with the response body
return originalJson(body);
};

next();
}
};
}
2 changes: 1 addition & 1 deletion apps/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { inspect } from "util";
import { caching } from "cache-manager";

import { EvmProvider } from "@zkchainhub/chain-providers/dist/src/index.js";
import { L1MetricsService } from "@zkchainhub/metrics";
import { CoingeckoProvider } from "@zkchainhub/pricing";
import { Logger } from "@zkchainhub/shared";

import { EvmProvider } from "../../../packages/chain-providers/dist/src/index.js";
import { App } from "./app.js";
import { config } from "./common/config/index.js";
import { MetricsController, MetricsRouter } from "./metrics/index.js";
Expand Down
5 changes: 3 additions & 2 deletions apps/api/src/metrics/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { z } from "zod";

import { ILogger } from "@zkchainhub/shared";

import { cacheMiddleware } from "../../common/middleware/cache.middleware.js";
import { BaseRouter } from "../../common/routes/baseRouter.js";
import { ChainNotFound, MetricsController } from "../index.js";

Expand All @@ -27,7 +28,7 @@ export class MetricsRouter extends BaseRouter {
* Retrieves the ecosystem information.
* @returns {Promise<EcosystemInfo>} The ecosystem information.
*/
this.router.get("/ecosystem", async (_req, res, next) => {
this.router.get("/ecosystem", cacheMiddleware(), async (_req, res, next) => {
try {
const data = await this.metricsController.getEcosystem();
res.json(data);
Expand All @@ -42,7 +43,7 @@ export class MetricsRouter extends BaseRouter {
* @param {number} chainId - The ID of the chain.
* @returns {Promise<ZKChainInfo>} The chain information.
*/
this.router.get("/zkchain/:chainId", async (req, res, next) => {
this.router.get("/zkchain/:chainId", cacheMiddleware(), async (req, res, next) => {
try {
const { params } = ChainIdSchema.parse(req);

Expand Down
2 changes: 1 addition & 1 deletion packages/metrics/src/l1/l1MetricsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
zeroAddress,
} from "viem";

import { EvmProvider } from "@zkchainhub/chain-providers/dist/src/index.js";
import { IPricingProvider } from "@zkchainhub/pricing";
import {
BatchesInfo,
Expand All @@ -27,7 +28,6 @@ import {
WETH,
} from "@zkchainhub/shared";

import { EvmProvider } from "../../../chain-providers/dist/src/index.js";
import {
AssetTvl,
bridgeHubAbi,
Expand Down
2 changes: 1 addition & 1 deletion packages/metrics/test/unit/l1/l1MetricsService.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Address, encodeFunctionData, erc20Abi, parseEther, zeroAddress } from "viem";
import { afterEach, describe, expect, it, Mocked, vi } from "vitest";

import { EvmProvider, MulticallNotFound } from "@zkchainhub/chain-providers/dist/src/index.js";
import { IPricingProvider } from "@zkchainhub/pricing";
import {
BatchesInfo,
Expand All @@ -15,7 +16,6 @@ import {
WETH,
} from "@zkchainhub/shared";

import { EvmProvider, MulticallNotFound } from "../../../../chain-providers/dist/src/index.js";
import {
bridgeHubAbi,
diamondProxyAbi,
Expand Down
23 changes: 23 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 66359db

Please sign in to comment.