-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: l2 metrics service #63
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./l2Metrics.service.js"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { isNativeError } from "util/types"; | ||
import { BaseError } from "viem"; | ||
|
||
import { ZKChainProvider } from "@zkchainhub/chain-providers"; | ||
import { ILogger } from "@zkchainhub/shared"; | ||
|
||
/** | ||
* Acts as a wrapper around Viem library to provide methods to interact with zkSync chains. | ||
*/ | ||
export class L2MetricsService { | ||
constructor( | ||
private readonly provider: ZKChainProvider, | ||
private readonly logger: ILogger, | ||
) {} | ||
|
||
/** | ||
* Retrieves the transactions per second (TPS) from the provider. | ||
* | ||
* @returns A promise that resolves to the number of transactions per second. | ||
*/ | ||
async tps(): Promise<number> { | ||
return this.provider.tps(); | ||
} | ||
|
||
/** | ||
* Retrieves the average block time from the provider. | ||
* | ||
* @returns A promise that resolves to the average block time as a number. | ||
*/ | ||
async avgBlockTime(): Promise<number> { | ||
return this.provider.avgBlockTime(); | ||
} | ||
|
||
/** | ||
* Retrieves the number of the last block in the chain. | ||
* | ||
* @returns A promise that resolves to a bigint representing the number of the last block. | ||
*/ | ||
async lastBlock(): Promise<bigint> { | ||
return this.provider.getBlockNumber(); | ||
} | ||
|
||
/** | ||
* Retrieves the last verified block based on the given lastVerifiedBatch. | ||
* | ||
* @param lastVerifiedBatch The number representing the last verified batch. | ||
* @returns A Promise that resolves to the number of the last verified block, or undefined if an error occurs. | ||
*/ | ||
async getLastVerifiedBlock(lastVerifiedBatch: number): Promise<number | undefined> { | ||
try { | ||
const [, endBlock] = await this.provider.getL1BatchBlockRange(lastVerifiedBatch); | ||
return endBlock; | ||
} catch (error) { | ||
if (error instanceof BaseError) { | ||
this.logger.error(error.message); | ||
} else if (isNativeError(error)) { | ||
this.logger.error(error); | ||
} | ||
return undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Following the way we are handling errors, for the rest of the methods. Should we throw here instead of returning undefined ? The lastVerifiedBlock is not an optional method on the front end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this got me thinking tbh, should we actually return return value | undefined for all methods? like an error on L2 shouldn't break anything as it's like not having rpc urls wdyt? |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { BaseError } from "viem"; | ||
import { beforeEach, describe, expect, it, vi } from "vitest"; | ||
|
||
import { ZKChainProvider } from "@zkchainhub/chain-providers"; | ||
import { ILogger } from "@zkchainhub/shared"; | ||
|
||
import { L2MetricsService } from "../../../src/l2/l2Metrics.service"; | ||
|
||
describe("L2MetricsService", () => { | ||
let service: L2MetricsService; | ||
let provider: ZKChainProvider; | ||
let logger: ILogger; | ||
|
||
beforeEach(() => { | ||
provider = { | ||
tps: vi.fn(), | ||
avgBlockTime: vi.fn(), | ||
getBlockNumber: vi.fn(), | ||
getL1BatchBlockRange: vi.fn(), | ||
} as unknown as ZKChainProvider; | ||
logger = { | ||
info: vi.fn(), | ||
warn: vi.fn(), | ||
error: vi.fn(), | ||
debug: vi.fn(), | ||
} as unknown as ILogger; | ||
service = new L2MetricsService(provider, logger); | ||
}); | ||
|
||
it("should create an instance of L2MetricsService", () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
|
||
describe("tps", () => { | ||
it("should return the TPS value", async () => { | ||
const expectedTps = 100; | ||
vi.spyOn(provider, "tps").mockResolvedValue(expectedTps); | ||
|
||
const result = await service.tps(); | ||
|
||
expect(result).toBe(expectedTps); | ||
expect(provider.tps).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe("avgBlockTime", () => { | ||
it("return the average block time", async () => { | ||
const expectedAvgBlockTime = 10; | ||
vi.spyOn(provider, "avgBlockTime").mockResolvedValue(expectedAvgBlockTime); | ||
|
||
const result = await service.avgBlockTime(); | ||
|
||
expect(result).toBe(expectedAvgBlockTime); | ||
expect(provider.avgBlockTime).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe("lastBlock", () => { | ||
it("return the last block number", async () => { | ||
const expectedLastBlock = 1000n; | ||
vi.spyOn(provider, "getBlockNumber").mockResolvedValue(expectedLastBlock); | ||
|
||
const result = await service.lastBlock(); | ||
|
||
expect(result).toBe(expectedLastBlock); | ||
expect(provider.getBlockNumber).toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe("getLastVerifiedBlock", () => { | ||
it("return the end block of the last verified batch", async () => { | ||
const lastVerifiedBatch = 5; | ||
const expectedEndBlock = 100; | ||
vi.spyOn(provider, "getL1BatchBlockRange").mockResolvedValue([0, expectedEndBlock]); | ||
|
||
const result = await service.getLastVerifiedBlock(lastVerifiedBatch); | ||
|
||
expect(result).toBe(expectedEndBlock); | ||
expect(provider.getL1BatchBlockRange).toHaveBeenCalledWith(lastVerifiedBatch); | ||
}); | ||
it("return undefined if an error occurs", async () => { | ||
const lastVerifiedBatch = 500; | ||
vi.spyOn(provider, "getL1BatchBlockRange").mockRejectedValue( | ||
new BaseError("Invalid batch number"), | ||
); | ||
|
||
const result = await service.getLastVerifiedBlock(lastVerifiedBatch); | ||
|
||
expect(result).toBeUndefined(); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any non-happy paths worth testing?