Skip to content
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

refactor: replace Service with Provider #53

Merged
merged 1 commit into from
Aug 22, 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ You can fetch metrics from the ZKsync ecosystem and ZK chains from L1 RPC (Ether
This repository is a monorepo consisting of 4 packages and 1 app:

- [`@zkchainhub/shared`](./packages/shared): A library for shared configurations, constants, types, etc.
- [`@zkchainhub/providers`](./packages/providers): A library that provides abstracted services over Viem providers to query blockchain data
- [`@zkchainhub/chain-providers`](./packages/chain-providers): A library that provides abstracted services over Viem providers to query blockchain data
- [`@zkchainhub/pricing`](./packages/pricing): An extensible library that provides Pricing services to fetch token prices. Currently, only Coingecko provider is developed
- [`@zkchainhub/metrics`](./packages/metrics): A library that provides different aggregated metrics from the ZKsync ecosystem and ZK chains.
- [`@zkchainhub/api`](./apps/api): An Express server that exposes an API where you can fetch information about ZKsync ecosystem and their chains, using the before mentioned libraries
Expand Down
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"dependencies": {
"@zkchainhub/metrics": "workspace:*",
"@zkchainhub/pricing": "workspace:*",
"@zkchainhub/providers": "workspace:*",
"@zkchainhub/chain-providers": "workspace:*",
"@zkchainhub/shared": "workspace:*",
"bignumber.js": "9.1.2",
"cache-manager": "5.7.6",
Expand Down
8 changes: 4 additions & 4 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { inspect } from "util";
import { caching } from "cache-manager";

import { L1MetricsService } from "@zkchainhub/metrics";
import { CoingeckoService } from "@zkchainhub/pricing";
import { EvmProviderService } from "@zkchainhub/providers";
import { CoingeckoProvider } from "@zkchainhub/pricing";
import { Logger } from "@zkchainhub/shared";

import { EvmProvider } from "../../../packages/chain-providers/dist/src/index.js";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { EvmProvider } from "../../../packages/chain-providers/dist/src/index.js";
import { EvmProvider } from "@zkchainhub/chain-providers";

import { App } from "./app.js";
import { config } from "./common/config/index.js";
import { MetricsController, MetricsRouter } from "./metrics/index.js";
Expand All @@ -18,8 +18,8 @@ const main = async (): Promise<void> => {
ttl: config.pricing.cacheOptions.ttl * 1000 /*milliseconds*/,
});

const evmProvider = new EvmProviderService(config.l1.rpcUrls, config.l1.chain, logger);
const pricingProvider = new CoingeckoService(
const evmProvider = new EvmProvider(config.l1.rpcUrls, config.l1.chain, logger);
const pricingProvider = new CoingeckoProvider(
{
apiBaseUrl: config.pricing.pricingOptions.apiBaseUrl,
apiKey: config.pricing.pricingOptions.apiKey,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@zkchainhub/providers",
"name": "@zkchainhub/chain-providers",
"version": "1.0.0",
"main": "./dist/src/index.js",
"type": "module",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ export {
RpcUrlsEmpty,
} from "./internal.js";

export { EvmProviderService, ZKChainProviderService } from "./internal.js";
export { EvmProvider, ZKChainProvider } from "./internal.js";
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
/**
* Acts as a wrapper around Viem library to provide methods to interact with an EVM-based blockchain.
*/
export class EvmProviderService {
export class EvmProvider {
private client: ReturnType<
typeof createPublicClient<FallbackTransport<HttpTransport[]>, Chain>
>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { GetL1BatchDetailsReturnType, PublicActionsL2, publicActionsL2 } from "v
import { ILogger } from "@zkchainhub/shared";

import { InvalidArgumentException } from "../internal.js";
import { EvmProviderService } from "./evmProvider.service.js";
import { EvmProvider } from "./evmProvider.service.js";

/**
* Acts as a wrapper around Viem library to provide methods to interact with ZK chains.
*/
export class ZKChainProviderService extends EvmProviderService {
export class ZKChainProvider extends EvmProvider {
private zkClient: Client<
FallbackTransport<HttpTransport[]>,
Chain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ILogger } from "@zkchainhub/shared";
import { arrayAbiFixture, structAbiFixture } from "../../../src/fixtures/batchRequest.fixture.js";
import {
DataDecodeException,
EvmProviderService,
EvmProvider,
MulticallNotFound,
RpcUrlsEmpty,
} from "../../../src/internal.js";
Expand Down Expand Up @@ -45,8 +45,8 @@ const testAbi = parseAbi([
"function tokenURI(uint256 tokenId) pure returns (string)",
]);

describe("EvmProviderService", () => {
let viemProvider: EvmProviderService | null = null;
describe("EvmProvider", () => {
let viemProvider: EvmProvider | null = null;
const defaultMockChain: viem.Chain = vi.mocked<viem.Chain>({
...localhost,
contracts: { multicall3: undefined },
Expand All @@ -60,19 +60,19 @@ describe("EvmProviderService", () => {
});

it("has a client property defined", () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
expect(viemProvider["client"]).toBeDefined();
});

it("throws RpcUrlsEmpty error if rpcUrls is empty", () => {
expect(() => {
new EvmProviderService([], defaultMockChain, mockLogger);
new EvmProvider([], defaultMockChain, mockLogger);
}).toThrowError(RpcUrlsEmpty);
});

describe("getBalance", () => {
it("should return the balance of the specified address", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const address = "0x123456789";
const expectedBalance = 100n;
vi.spyOn(mockClient, "getBalance").mockResolvedValue(expectedBalance);
Expand All @@ -86,7 +86,7 @@ describe("EvmProviderService", () => {

describe("getBlockNumber", () => {
it("should return the current block number", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const expectedBlockNumber = 1000n;
vi.spyOn(mockClient, "getBlockNumber").mockResolvedValue(expectedBlockNumber);

Expand All @@ -98,7 +98,7 @@ describe("EvmProviderService", () => {

describe("getGasPrice", () => {
it("should return the current gas price", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const expectedGasPrice = BigInt(100);
// Mock the getGasPrice method of the Viem client
vi.spyOn(mockClient, "getGasPrice").mockResolvedValue(expectedGasPrice);
Expand All @@ -111,7 +111,7 @@ describe("EvmProviderService", () => {

describe("estimateGas", () => {
it("return the estimated gas for the given transaction", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const args = vi.mocked<viem.EstimateGasParameters<typeof localhost>>({
account: "0xffff",
to: viem.zeroAddress,
Expand All @@ -130,7 +130,7 @@ describe("EvmProviderService", () => {

describe("getStorageAt", () => {
it("should return the value of the storage slot at the given address and slot number", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const address = "0x123456789";
const slot = 1;
const expectedValue = "0xabcdef";
Expand All @@ -143,7 +143,7 @@ describe("EvmProviderService", () => {
});

it("should return the value of the storage slot at the given address and slot value", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const address = "0x123456789";
const slot = "0x12";
const expectedValue = "0xabcdef";
Expand All @@ -156,7 +156,7 @@ describe("EvmProviderService", () => {
});

it("should throw an error if the slot is not a positive integer", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const address = "0x123456789";
const slot = -1;

Expand All @@ -168,7 +168,7 @@ describe("EvmProviderService", () => {

describe("readContract", () => {
it("should call the readContract method of the Viem client with the correct arguments", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const contractAddress = "0x123456789";
const abi = testAbi;
const functionName = "balanceOf";
Expand All @@ -188,7 +188,7 @@ describe("EvmProviderService", () => {
});

it("should call the readContract method of the Viem client with the correct arguments when args are provided", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const contractAddress = "0x123456789";
const functionName = "tokenURI";
const args = [1n] as const;
Expand Down Expand Up @@ -216,7 +216,7 @@ describe("EvmProviderService", () => {

describe("batchRequest", () => {
it("should properly encode bytecode data and decode return data from batch request call", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const returnAbiParams = viem.parseAbiParameters([
"TokenData[] returnData",
"struct TokenData { uint8 tokenDecimals; string tokenSymbol; string tokenName; }",
Expand Down Expand Up @@ -245,7 +245,7 @@ describe("EvmProviderService", () => {
});

it("should fail if no data is returned", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const returnAbiParams = viem.parseAbiParameters([
"TokenData[] returnData",
"struct TokenData { uint8 tokenDecimals; string tokenSymbol; string tokenName; }",
Expand All @@ -264,7 +264,7 @@ describe("EvmProviderService", () => {
});

it("should fail if decoded data does not match validator (missing struct fields)", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
// this schema is incorrect, it should have 3 fields instead of 2
const returnAbiParams = viem.parseAbiParameters([
"WrongTokenData[] returnData",
Expand All @@ -286,7 +286,7 @@ describe("EvmProviderService", () => {
});

it("should fail if decoded data does not match validator (not struct vs struct)", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
// this schema is incorrect, it should have 3 fields instead of 2
const returnAbiParams = viem.parseAbiParameters("uint8 decimals, address[] owners");

Expand All @@ -305,7 +305,7 @@ describe("EvmProviderService", () => {
});

it("should properly decode address[]", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const returnAbiParams = viem.parseAbiParameters("address[]");

vi.spyOn(mockClient, "call").mockResolvedValue({ data: arrayAbiFixture.returnData });
Expand All @@ -327,7 +327,7 @@ describe("EvmProviderService", () => {
contracts: { multicall3: { address: "0x123456789" } },
});

viemProvider = new EvmProviderService(defaultRpcUrls, mockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, mockChain, mockLogger);
const contracts = [
{
address: "0x123456789",
Expand Down Expand Up @@ -363,7 +363,7 @@ describe("EvmProviderService", () => {
});

it("throws a MulticallNotFound error if the Multicall contract is not found for the chain", async () => {
viemProvider = new EvmProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
viemProvider = new EvmProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const contracts = [
{
address: "0x123456789",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";

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

import {
InvalidArgumentException,
RpcUrlsEmpty,
ZKChainProviderService,
} from "../../../src/internal.js";
import { InvalidArgumentException, RpcUrlsEmpty, ZKChainProvider } from "../../../src/internal.js";

export const mockLogger: ILogger = {
info: vi.fn(),
Expand All @@ -18,8 +14,8 @@ export const mockLogger: ILogger = {
debug: vi.fn(),
};

describe("ZKChainProviderService", () => {
let zkProvider: ZKChainProviderService;
describe("ZKChainProvider", () => {
let zkProvider: ZKChainProvider;
const defaultMockChain = localhost;
const defaultRpcUrls = ["http://localhost:8545"];

Expand All @@ -28,19 +24,19 @@ describe("ZKChainProviderService", () => {
});

it("has a zkclient property defined", () => {
zkProvider = new ZKChainProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
zkProvider = new ZKChainProvider(defaultRpcUrls, defaultMockChain, mockLogger);
expect(zkProvider["zkClient"]).toBeDefined();
});

it("throws RpcUrlsEmpty error if rpcUrls is empty", () => {
expect(() => {
new ZKChainProviderService([], localhost, mockLogger);
new ZKChainProvider([], localhost, mockLogger);
}).toThrowError(RpcUrlsEmpty);
});

describe("avgBlockTime", () => {
it("should return the average block time over the given range", async () => {
zkProvider = new ZKChainProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
zkProvider = new ZKChainProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const currentBlockNumber = 1000;
const range = 100;
const currentBlockTimestamp = { timestamp: BigInt(123234345) };
Expand Down Expand Up @@ -68,7 +64,7 @@ describe("ZKChainProviderService", () => {
});

it("should throw an InvalidArgumentException if the range is less than 1", async () => {
zkProvider = new ZKChainProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
zkProvider = new ZKChainProvider(defaultRpcUrls, defaultMockChain, mockLogger);
await expect(zkProvider.avgBlockTime(0)).rejects.toThrowError(
new InvalidArgumentException("range for avgBlockTime should be >= 1"),
);
Expand All @@ -77,7 +73,7 @@ describe("ZKChainProviderService", () => {

describe("tps", () => {
it("should return the transactions per second (TPS)", async () => {
zkProvider = new ZKChainProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
zkProvider = new ZKChainProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const currentBatchNumber = 1000; // 1000 in hexadecimal
const currentBatchDetails = { l2TxCount: 200, timestamp: 123234345 };
const prevBatchDetails = { timestamp: 123123123 };
Expand All @@ -103,7 +99,7 @@ describe("ZKChainProviderService", () => {
});

it("should handle the case when there are no transactions", async () => {
zkProvider = new ZKChainProviderService(defaultRpcUrls, defaultMockChain, mockLogger);
zkProvider = new ZKChainProvider(defaultRpcUrls, defaultMockChain, mockLogger);
const currentBatchNumber = 1000; // 1000 in hexadecimal
const currentBatchDetails = { l2TxCount: 0, timestamp: 123234345 };
const prevBatchDetails = { timestamp: 123123123 };
Expand Down
4 changes: 2 additions & 2 deletions packages/metadata/src/external.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export type { IMetadataService } from "./internal.js";
export type { IMetadataProvider } from "./internal.js";

export { StaticMetadataService, GithubMetadataService } from "./internal.js";
export { StaticMetadataProvider, GithubMetadataProvider } from "./internal.js";
2 changes: 1 addition & 1 deletion packages/metadata/src/interfaces/metadata.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Token, TokenType, ZKChainMetadata } from "@zkchainhub/shared";

export interface IMetadataService {
export interface IMetadataProvider {
getChainsMetadata(): Promise<ZKChainMetadata>;
getTokensMetadata(): Promise<Token<TokenType>[]>;
}
2 changes: 1 addition & 1 deletion packages/metadata/src/internal.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from "./interfaces/index.js";
export * from "./services/index.js";
export * from "./providers/index.js";
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Token, TokenType, ZKChainMetadata } from "@zkchainhub/shared";

import { IMetadataService } from "../interfaces/index.js";
import { IMetadataProvider } from "../interfaces/index.js";

export class GithubMetadataService implements IMetadataService {
export class GithubMetadataProvider implements IMetadataProvider {
async getChainsMetadata(): Promise<ZKChainMetadata> {
//TODO: Implement this method
throw new Error("Method not implemented.");
Expand Down
2 changes: 2 additions & 0 deletions packages/metadata/src/providers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./githubMetadata.provider.js";
export * from "./staticMetadata.provider.js";
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Token, tokens, TokenType, ZKChainMetadata, zkChainsMetadata } from "@zkchainhub/shared";

import { IMetadataService } from "../interfaces/index.js";
import { IMetadataProvider } from "../interfaces/index.js";

export class StaticMetadataService implements IMetadataService {
export class StaticMetadataProvider implements IMetadataProvider {
async getChainsMetadata(): Promise<ZKChainMetadata> {
return structuredClone(zkChainsMetadata);
}
Expand Down
2 changes: 0 additions & 2 deletions packages/metadata/src/services/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";

import { Token, TokenType, ZKChainMetadata } from "@zkchainhub/shared";

import { StaticMetadataService } from "../../../src/internal.js";
import { StaticMetadataProvider } from "../../../src/internal.js";

describe("StaticMetadataService", () => {
let metadataService: StaticMetadataService;
describe("StaticMetadataProvider", () => {
let metadataService: StaticMetadataProvider;

beforeEach(() => {
metadataService = new StaticMetadataService();
metadataService = new StaticMetadataProvider();
});

describe("getChainsMetadata", () => {
Expand Down
Loading