diff --git a/apps/api/src/api.module.ts b/apps/api/src/api.module.ts index 24020a3..09ad70c 100644 --- a/apps/api/src/api.module.ts +++ b/apps/api/src/api.module.ts @@ -3,13 +3,24 @@ import { ProvidersModule } from "@packages/providers"; import { ApiController } from "./api.controller"; import { RequestLoggerMiddleware } from "./common/middleware/request.middleware"; +import { MetricsController } from "./metrics/metrics.controller"; +/** + * The main API module of the application. + * Here we import all required modules and register the controllers for the ZKchainHub API. + */ @Module({ imports: [ProvidersModule], - controllers: [ApiController], + controllers: [ApiController, MetricsController], providers: [], }) export class ApiModule implements NestModule { + /** + * Configures middleware for the module. + * Applies RequestLoggerMiddleware to all routes except '/docs' and '/docs/(.*)'. + * + * @param {MiddlewareConsumer} consumer - The middleware consumer provided by NestJS. + */ configure(consumer: MiddlewareConsumer) { consumer.apply(RequestLoggerMiddleware).exclude("/docs", "/docs/(.*)").forRoutes("*"); } diff --git a/apps/api/src/common/pipes/parsePositiveInt.pipe.ts b/apps/api/src/common/pipes/parsePositiveInt.pipe.ts new file mode 100644 index 0000000..aaa7c0d --- /dev/null +++ b/apps/api/src/common/pipes/parsePositiveInt.pipe.ts @@ -0,0 +1,26 @@ +import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from "@nestjs/common"; + +/** + * A pipe that transforms and validates input strings to positive integers. + * @implements {PipeTransform} + */ +@Injectable() +export class ParsePositiveIntPipe implements PipeTransform { + /** + * Transforms and validates the input value. + * + * @param {string} value - The input value to be transformed and validated. + * @param {ArgumentMetadata} metadata - Metadata about the transformed argument. + * @returns {number} The parsed positive integer. + * @throws {BadRequestException} If the input is not a valid positive integer. + */ + transform(value: string, metadata: ArgumentMetadata): number { + const parsedValue = parseInt(value, 10); + if (isNaN(parsedValue) || parsedValue < 0) { + throw new BadRequestException( + `Validation failed: Parameter ${metadata.data} must be a positive integer`, + ); + } + return parsedValue; + } +} diff --git a/apps/api/src/metrics/metrics.controller.spec.ts b/apps/api/src/metrics/metrics.controller.spec.ts new file mode 100644 index 0000000..2343618 --- /dev/null +++ b/apps/api/src/metrics/metrics.controller.spec.ts @@ -0,0 +1,41 @@ +import { Test, TestingModule } from "@nestjs/testing"; + +import { MetricsController } from "./metrics.controller"; +import { getEcosystemInfo, getZKChainInfo } from "./mocks/metrics.mock"; + +describe("MetricsController", () => { + let controller: MetricsController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [MetricsController], + }).compile(); + + controller = module.get(MetricsController); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); + + describe("getEcosystem", () => { + it("should return the ecosystem information", async () => { + const expectedInfo = getEcosystemInfo(); + + const result = await controller.getEcosystem(); + + expect(result).toEqual(expectedInfo); + }); + }); + + describe("getChain", () => { + it("should return the chain information for the specified chain ID", async () => { + const chainId = 123; + const expectedInfo = getZKChainInfo(chainId); + + const result = await controller.getChain(chainId); + + expect(result).toEqual(expectedInfo); + }); + }); +}); diff --git a/apps/api/src/metrics/metrics.controller.ts b/apps/api/src/metrics/metrics.controller.ts new file mode 100644 index 0000000..b39842e --- /dev/null +++ b/apps/api/src/metrics/metrics.controller.ts @@ -0,0 +1,34 @@ +import { Controller, Get, Param } from "@nestjs/common"; +import { ApiResponse, ApiTags } from "@nestjs/swagger"; +import { ZKChainInfo } from "@shared/dtos/dto/chain.dto"; + +import { ParsePositiveIntPipe } from "../common/pipes/parsePositiveInt.pipe"; +import { getEcosystemInfo, getZKChainInfo } from "./mocks/metrics.mock"; + +@ApiTags("metrics") +@Controller("metrics") +/** + * Controller for handling metrics related endpoints. + */ +export class MetricsController { + /** + * Retrieves the ecosystem information. + * @returns {Promise} The ecosystem information. + */ + @Get("/ecosystem") + public async getEcosystem() { + return getEcosystemInfo(); + } + + /** + * Retrieves the chain information for the specified chain ID. + * @param {number} chainId - The ID of the chain. + * @returns {Promise} The chain information. + */ + + @ApiResponse({ status: 200, type: ZKChainInfo }) + @Get("zkchain/:chainId") + public async getChain(@Param("chainId", new ParsePositiveIntPipe()) chainId: number) { + return getZKChainInfo(chainId); + } +} diff --git a/apps/api/src/metrics/mocks/metrics.mock.ts b/apps/api/src/metrics/mocks/metrics.mock.ts new file mode 100644 index 0000000..56d930d --- /dev/null +++ b/apps/api/src/metrics/mocks/metrics.mock.ts @@ -0,0 +1,104 @@ +import { EcosystemInfo } from "@shared/dtos"; +import { ZKChainInfo } from "@shared/dtos/dto/chain.dto"; +import { L2ChainInfo } from "@shared/dtos/dto/l2Metrics.dto"; +import { Metadata } from "@shared/dtos/dto/metadata.dto"; + +export const getEcosystemInfo = () => { + const mock = new EcosystemInfo({ + l1Tvl: { ETH: 1000000, USDC: 500000 }, + ethGasInfo: { + gasPrice: 50, + ethTransfer: 21000, + erc20Transfer: 65000, + }, + zkChains: [ + { + chainId: 0, + chainType: "Rollup", + nativeToken: "ETH", + tvl: 1000000, + metadata: true, + rpc: true, + }, + { + chainId: 1, + chainType: "Validium", + nativeToken: "ETH", + tvl: 500000, + metadata: true, + rpc: false, + }, + { + chainId: 2, + chainType: "Rollup", + tvl: 300000, + metadata: false, + rpc: true, + }, + { + chainId: 3, + chainType: "Rollup", + tvl: 10000, + metadata: false, + rpc: false, + }, + ], + }); + return mock; +}; + +const mockMetadata: Metadata = { + iconUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/24091.png", + chainName: "ZKsyncERA", + publicRpcs: [ + { url: "https://mainnet.era.zksync.io", status: true }, + { url: "https://1rpc.io/zksync2-era", status: true }, + { url: "https://zksync.drpc.org", status: false }, + ], + explorerUrl: "https://explorer.zksync.io/", + launchDate: 1679626800, + environment: "mainnet", + nativeToken: "ETH", +}; + +const mockL2Info: L2ChainInfo = { + tps: 10000000, + avgBlockTime: 12, + lastBlock: 1000000, + lastBlockVerified: 999999, +}; + +export const getZKChainInfo = (chainId: number): ZKChainInfo => { + const mock = new ZKChainInfo({ + chainType: "Rollup", + tvl: { ETH: 1000000, USDC: 500000 }, + batchesInfo: { + commited: 100, + verified: 90, + proved: 80, + }, + feeParams: { + batchOverheadL1Gas: 50000, + maxPubdataPerBatch: 120000, + maxL2GasPerBatch: 10000000, + priorityTxMaxPubdata: 15000, + minimalL2GasPrice: 0.25, + }, + }); + switch (chainId) { + case 0: + mock.metadata = mockMetadata; + mock.l2ChainInfo = mockL2Info; + break; + case 1: + mock.metadata = mockMetadata; + break; + case 2: + mock.l2ChainInfo = mockL2Info; + break; + default: + break; + } + + return mock; +}; diff --git a/apps/api/test/jest-e2e.json b/apps/api/test/jest-e2e.json index adbc02a..3b1d283 100644 --- a/apps/api/test/jest-e2e.json +++ b/apps/api/test/jest-e2e.json @@ -8,6 +8,7 @@ "^.+\\.(t|j)s$": "ts-jest" }, "moduleNameMapper": { - "^@packages/providers(|/.*)$": "/libs/providers/src/$1" + "^@packages/providers(|/.*)$": "/libs/providers/src/$1", + "^@shared/dtos(|/.*)$": "/libs/dtos/src/$1" } } diff --git a/apps/api/test/metrics.e2e-spec.ts b/apps/api/test/metrics.e2e-spec.ts new file mode 100644 index 0000000..a96fb2d --- /dev/null +++ b/apps/api/test/metrics.e2e-spec.ts @@ -0,0 +1,116 @@ +import { INestApplication } from "@nestjs/common"; +import { Test, TestingModule } from "@nestjs/testing"; +import request from "supertest"; + +import { ApiModule } from "../src/api.module"; + +describe("MetricsController (e2e)", () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [ApiModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + afterEach(async () => { + await app.close(); + }); + + describe("/ecosystem (GET)", () => { + it("/ecosystem (GET)", () => { + return request(app.getHttpServer()) + .get("/metrics/ecosystem") + .expect(200) + .expect(({ body }) => { + expect(body.l1Tvl).toBeDefined(); + expect(body.ethGasInfo).toBeDefined(); + expect(body.zkChains).toBeDefined(); + expect(body.zkChains.length).toBeGreaterThan(0); + }); + }); + }); + + describe("/chain/:chainId (GET)", () => { + it("correct request for RPC + METADATA", () => { + return request(app.getHttpServer()) + .get("/metrics/zkchain/0") + .expect(200) + .expect(({ body }) => { + expect(body.chainType).toBeDefined(); + expect(body.tvl).toBeDefined(); + expect(body.batchesInfo).toBeDefined(); + expect(body.feeParams).toBeDefined(); + expect(body.metadata).toBeDefined(); + expect(body.l2ChainInfo).toBeDefined(); + }); + }); + + it("correct request for METADATA", () => { + return request(app.getHttpServer()) + .get("/metrics/zkchain/1") + .expect(200) + .expect(({ body }) => { + expect(body.chainType).toBeDefined(); + expect(body.tvl).toBeDefined(); + expect(body.batchesInfo).toBeDefined(); + expect(body.feeParams).toBeDefined(); + expect(body.metadata).toBeDefined(); + expect(body.l2ChainInfo).toBeUndefined(); + }); + }); + + it("correct request for RPC", () => { + return request(app.getHttpServer()) + .get("/metrics/zkchain/2") + .expect(200) + .expect(({ body }) => { + expect(body.chainType).toBeDefined(); + expect(body.tvl).toBeDefined(); + expect(body.batchesInfo).toBeDefined(); + expect(body.feeParams).toBeDefined(); + expect(body.metadata).toBeUndefined(); + expect(body.l2ChainInfo).toBeDefined(); + }); + }); + + it("correct request for NO RPC + METADATA", () => { + return request(app.getHttpServer()) + .get("/metrics/zkchain/3") + .expect(200) + .expect(({ body }) => { + expect(body.chainType).toBeDefined(); + expect(body.tvl).toBeDefined(); + expect(body.batchesInfo).toBeDefined(); + expect(body.feeParams).toBeDefined(); + expect(body.metadata).toBeUndefined(); + expect(body.l2ChainInfo).toBeUndefined(); + }); + }); + + it("invalid negative number for chain id", () => { + return request(app.getHttpServer()) + .get("/metrics/zkchain/-1") + .expect(400) + .expect(({ body }) => { + expect(body.message).toEqual( + "Validation failed: Parameter chainId must be a positive integer", + ); + }); + }); + + it("not a number for chain id", () => { + return request(app.getHttpServer()) + .get("/metrics/zkchain/notanumber") + .expect(400) + .expect(({ body }) => { + expect(body.message).toEqual( + "Validation failed: Parameter chainId must be a positive integer", + ); + }); + }); + }); +}); diff --git a/libs/dtos/src/dto/chain.dto.ts b/libs/dtos/src/dto/chain.dto.ts new file mode 100644 index 0000000..65c2f5d --- /dev/null +++ b/libs/dtos/src/dto/chain.dto.ts @@ -0,0 +1,71 @@ +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; +import { AssetDistribution, BatchesInfo, FeeParams, L2ChainInfo, Metadata } from "@shared/dtos/dto"; +import { Chains, ChainType } from "@shared/dtos/types"; + +/** + * ZKChainInfo class representing the ZK chain information. + */ +export class ZKChainInfo { + /** + * The type of chain. + * @type {ChainType} + * @memberof ZKChainInfo + */ + @ApiProperty({ enum: Chains, enumName: "ChainType" }) + chainType: ChainType; + + /** + * A map of asset names to their respective amounts. + * @type {AssetDistribution} + * @memberof ZKChainInfo + * @example { ETH: 1000000, ZK: 500000 } + */ + @ApiProperty({ + example: { ETH: 1000000, ZK: 500000 }, + description: "A map of asset names to their respective amounts", + additionalProperties: { + type: "number", + }, + }) + tvl: AssetDistribution; + + /** + * Optional batches information. + * @type {BatchesInfo} + * @memberof ZKChainInfo + */ + @ApiPropertyOptional() + batchesInfo?: BatchesInfo; + + /** + * The fee parameters. + * @type {FeeParams} + * @memberof ZKChainInfo + */ + feeParams: FeeParams; + + /** + * Optional metadata. + * @type {Metadata} + * @memberof ZKChainInfo + */ + @ApiPropertyOptional({ type: Metadata }) + metadata?: Metadata; + + /** + * Optional Layer 2 chain information. + * @type {L2ChainInfo} + * @memberof ZKChainInfo + */ + @ApiPropertyOptional() + l2ChainInfo?: L2ChainInfo; + + constructor(data: ZKChainInfo) { + this.chainType = data.chainType; + this.tvl = data.tvl; + this.batchesInfo = data.batchesInfo; + this.feeParams = data.feeParams; + this.metadata = data.metadata; + this.l2ChainInfo = data.l2ChainInfo; + } +} diff --git a/libs/dtos/src/dto/ecosystem.dto.ts b/libs/dtos/src/dto/ecosystem.dto.ts new file mode 100644 index 0000000..67419fe --- /dev/null +++ b/libs/dtos/src/dto/ecosystem.dto.ts @@ -0,0 +1,105 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { AssetDistribution, EthGasInfo } from "@shared/dtos/dto"; +import { Chains, ChainType } from "@shared/dtos/types"; + +/** + * EcosystemInfo class representing the information about the ecosystem. + */ +export class EcosystemInfo { + /** + * A map of asset names to their respective amounts for L1. + * @type {AssetDistribution} + * @memberof EcosystemInfo + * @example { ETH: 1000000, ZK: 500000 } + */ + @ApiProperty({ + example: { ETH: 1000000, ZK: 500000 }, + description: "A map of asset names to their respective amounts", + additionalProperties: { + type: "number", + }, + }) + l1Tvl: AssetDistribution; + + /** + * The Ethereum gas information. + * @type {EthGasInfo} + * @memberof EcosystemInfo + */ + ethGasInfo: EthGasInfo; + + /** + * An array of ZK chain summaries. + * @type {ZKChainSummary[]} + * @memberof EcosystemInfo + */ + @ApiProperty({ isArray: true }) + zkChains: ZKChainSummary[]; + + /** + * Constructs an instance of the EcosystemInfo class. + * @param {EcosystemInfo} data - The data to initialize the instance with. + */ + constructor(data: EcosystemInfo) { + this.l1Tvl = data.l1Tvl; + this.ethGasInfo = data.ethGasInfo; + this.zkChains = data.zkChains; + } +} + +/** + * ZKChainSummary class representing the summary information of a ZK chain. + */ +export class ZKChainSummary { + /** + * The ID of the chain. + * @type {number} + * @memberof ZKChainSummary + */ + chainId: number; + + /** + * The type of chain. + * @type {ChainType} + * @memberof ZKChainSummary + */ + @ApiProperty({ enum: Chains, enumName: "ChainType" }) + chainType: ChainType; + + /** + * The native token of the chain (optional). + * @type {string} + * @memberof ZKChainSummary + */ + nativeToken?: string; + + /** + * The total value locked in the chain. + * @type {number} + * @memberof ZKChainSummary + */ + tvl: number; + + /** + * Metadata flag (optional). + * @type {boolean} + * @memberof ZKChainSummary + */ + metadata?: boolean; + + /** + * RPC flag (optional). + * @type {boolean} + * @memberof ZKChainSummary + */ + rpc?: boolean; + + constructor(data: ZKChainSummary) { + this.chainId = data.chainId; + this.chainType = data.chainType; + this.nativeToken = data.nativeToken; + this.tvl = data.tvl; + this.metadata = data.metadata; + this.rpc = data.rpc; + } +} diff --git a/libs/dtos/src/dto/index.ts b/libs/dtos/src/dto/index.ts new file mode 100644 index 0000000..83e74fb --- /dev/null +++ b/libs/dtos/src/dto/index.ts @@ -0,0 +1,5 @@ +export * from "./l2Metrics.dto"; +export * from "./metadata.dto"; +export * from "./l1Metrics.dto"; +export * from "./chain.dto"; +export * from "./ecosystem.dto"; diff --git a/libs/dtos/src/dto/l1Metrics.dto.ts b/libs/dtos/src/dto/l1Metrics.dto.ts new file mode 100644 index 0000000..8bff7a1 --- /dev/null +++ b/libs/dtos/src/dto/l1Metrics.dto.ts @@ -0,0 +1,126 @@ +/** + * AssetDistribution class representing a map of asset names to their respective amounts. + */ +export class AssetDistribution { + [asset: string]: number; +} + +/** + * BatchesInfo class representing information about batches. + */ +export class BatchesInfo { + /** + * The number of committed batches. + * @type {number} + * @memberof BatchesInfo + */ + commited: number; + + /** + * The number of verified batches. + * @type {number} + * @memberof BatchesInfo + */ + verified: number; + + /** + * The number of proved batches. + * @type {number} + * @memberof BatchesInfo + */ + proved: number; + + /** + * Constructs an instance of the BatchesInfo class. + * @param {BatchesInfo} data - The data to initialize the instance with. + */ + constructor(data: BatchesInfo) { + this.commited = data.commited; + this.verified = data.verified; + this.proved = data.proved; + } +} + +/** + * EthGasInfo class representing information about Ethereum gas. + */ +export class EthGasInfo { + /** + * The gas price. + * @type {number} + * @memberof EthGasInfo + */ + gasPrice: number; + + /** + * The gas cost for ETH transfer. + * @type {number} + * @memberof EthGasInfo + */ + ethTransfer: number; + + /** + * The gas cost for ERC20 transfer. + * @type {number} + * @memberof EthGasInfo + */ + erc20Transfer: number; + + constructor(data: EthGasInfo) { + this.gasPrice = data.gasPrice; + this.ethTransfer = data.ethTransfer; + this.erc20Transfer = data.erc20Transfer; + } +} + +/** + * FeeParams class representing fee parameters. + */ +export class FeeParams { + /** + * The overhead L1 gas for batch. + * @type {number} + * @memberof FeeParams + */ + batchOverheadL1Gas: number; + + /** + * The maximum pubdata per batch. + * @type {number} + * @memberof FeeParams + */ + maxPubdataPerBatch: number; + + /** + * The maximum L2 gas per batch. + * @type {number} + * @memberof FeeParams + */ + maxL2GasPerBatch: number; + + /** + * The maximum pubdata for priority transactions. + * @type {number} + * @memberof FeeParams + */ + priorityTxMaxPubdata: number; + + /** + * The minimal L2 gas price. + * @type {number} + * @memberof FeeParams + */ + minimalL2GasPrice: number; + + /** + * Constructs an instance of the FeeParams class. + * @param {FeeParams} data - The data to initialize the instance with. + */ + constructor(data: FeeParams) { + this.batchOverheadL1Gas = data.batchOverheadL1Gas; + this.maxPubdataPerBatch = data.maxPubdataPerBatch; + this.maxL2GasPerBatch = data.maxL2GasPerBatch; + this.priorityTxMaxPubdata = data.priorityTxMaxPubdata; + this.minimalL2GasPrice = data.minimalL2GasPrice; + } +} diff --git a/libs/dtos/src/dto/l2Metrics.dto.ts b/libs/dtos/src/dto/l2Metrics.dto.ts new file mode 100644 index 0000000..c0b150a --- /dev/null +++ b/libs/dtos/src/dto/l2Metrics.dto.ts @@ -0,0 +1,39 @@ +/** + * L2ChainInfo class representing Layer 2 chain information. + */ +export class L2ChainInfo { + /** + * Transactions per second. + * @type {number} + * @memberof L2ChainInfo + */ + tps: number; + + /** + * Average block time in seconds. + * @type {number} + * @memberof L2ChainInfo + */ + avgBlockTime: number; + + /** + * The number of the last block. + * @type {number} + * @memberof L2ChainInfo + */ + lastBlock: number; + + /** + * The number of the last verified block. + * @type {number} + * @memberof L2ChainInfo + */ + lastBlockVerified: number; + + constructor(data: L2ChainInfo) { + this.tps = data.tps; + this.avgBlockTime = data.avgBlockTime; + this.lastBlock = data.lastBlock; + this.lastBlockVerified = data.lastBlockVerified; + } +} diff --git a/libs/dtos/src/dto/metadata.dto.ts b/libs/dtos/src/dto/metadata.dto.ts new file mode 100644 index 0000000..365bd6f --- /dev/null +++ b/libs/dtos/src/dto/metadata.dto.ts @@ -0,0 +1,87 @@ +/** + * RPC class representing the RPC information. + */ +export class RPC { + /** + * The URL of the RPC. + * @type {string} + * @memberof RPC + */ + url: string; + + /** + * The status of the RPC (optional). + * @type {boolean} + * @memberof RPC + */ + status?: boolean; + + constructor(data: RPC) { + this.url = data.url; + this.status = data.status; + } +} + +/** + * Metadata class representing the metadata information. + */ +export class Metadata { + /** + * The URL of the chain's icon (optional). + * @type {string} + * @memberof Metadata + */ + iconUrl?: string; + + /** + * The name of the chain. + * @type {string} + * @memberof Metadata + */ + chainName: string; + + /** + * An array of public RPCs. + * @type {RPC[]} + * @memberof Metadata + */ + publicRpcs: RPC[]; + + /** + * The URL of the chain's explorer. + * @type {string} + * @memberof Metadata + */ + explorerUrl: string; + + /** + * The launch date of the chain (timestamp). + * @type {number} + * @memberof Metadata + */ + launchDate: number; + + /** + * The environment of the chain (e.g., mainnet, testnet). + * @type {string} + * @memberof Metadata + */ + environment: string; + + /** + * The native token of the chain. + * @type {string} + * @memberof Metadata + */ + nativeToken: string; + + constructor(data: Metadata) { + this.iconUrl = data.iconUrl; + this.chainName = data.chainName; + this.publicRpcs = data.publicRpcs; + this.explorerUrl = data.explorerUrl; + this.launchDate = data.launchDate; + this.environment = data.environment; + this.nativeToken = data.nativeToken; + } +} diff --git a/libs/dtos/src/index.ts b/libs/dtos/src/index.ts new file mode 100644 index 0000000..c29f47c --- /dev/null +++ b/libs/dtos/src/index.ts @@ -0,0 +1,2 @@ +export * from "./dto"; +export * from "./types"; diff --git a/libs/dtos/src/types/index.ts b/libs/dtos/src/types/index.ts new file mode 100644 index 0000000..642a22c --- /dev/null +++ b/libs/dtos/src/types/index.ts @@ -0,0 +1 @@ +export * from "./rollup.type"; diff --git a/libs/dtos/src/types/rollup.type.ts b/libs/dtos/src/types/rollup.type.ts new file mode 100644 index 0000000..37e7549 --- /dev/null +++ b/libs/dtos/src/types/rollup.type.ts @@ -0,0 +1,12 @@ +/** + * An array of supported chain types. + * @readonly + */ +export const Chains = ["Rollup", "Validium"] as const; + +/** + * Represents the possible chain types. + * This type is derived from the {@link Chains} array. + * @typedef {typeof Chains[number]} ChainType + */ +export type ChainType = (typeof Chains)[number]; diff --git a/libs/dtos/tsconfig.lib.json b/libs/dtos/tsconfig.lib.json new file mode 100644 index 0000000..3090902 --- /dev/null +++ b/libs/dtos/tsconfig.lib.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": true, + "outDir": "../../dist/libs/dtos" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test", "**/*spec.ts"] +} diff --git a/libs/providers/src/evmProvider.service.ts b/libs/providers/src/evmProvider.service.ts index d828acc..6bd3ea3 100644 --- a/libs/providers/src/evmProvider.service.ts +++ b/libs/providers/src/evmProvider.service.ts @@ -1,7 +1,14 @@ import { Injectable } from "@nestjs/common"; +/** + * EvmProviderService provides methods to interact with an EVM-based blockchain. + */ @Injectable() export class EvmProviderService { + /** + * Retrieves the total value locked (TVL) from the EVM-based blockchain. + * @returns {Promise} The TVL value. + */ async getTvl() { return 1; } diff --git a/libs/providers/src/providers.module.ts b/libs/providers/src/providers.module.ts index 686bcbf..726bca1 100644 --- a/libs/providers/src/providers.module.ts +++ b/libs/providers/src/providers.module.ts @@ -2,6 +2,10 @@ import { Module } from "@nestjs/common"; import { EvmProviderService } from "./evmProvider.service"; +/** + * Module for managing provider services. + * This module exports Services for interacting with EVM-based blockchains. + */ @Module({ providers: [EvmProviderService], exports: [EvmProviderService], diff --git a/nest-cli.json b/nest-cli.json index e966f90..db2e7fb 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -5,7 +5,16 @@ "compilerOptions": { "deleteOutDir": true, "webpack": true, - "tsConfigPath": "apps/api/tsconfig.app.json" + "tsConfigPath": "apps/api/tsconfig.app.json", + "plugins": [ + { + "name": "@nestjs/swagger", + "options": { + "classValidatorShim": false, + "introspectComments": true + } + } + ] }, "monorepo": true, "root": "apps/api", @@ -16,16 +25,7 @@ "entryFile": "main", "sourceRoot": "apps/api/src", "compilerOptions": { - "tsConfigPath": "apps/api/tsconfig.app.json", - "plugins": [ - { - "name": "@nestjs/swagger", - "options": { - "classValidatorShim": false, - "introspectComments": true - } - } - ] + "tsConfigPath": "apps/api/tsconfig.app.json" } }, "providers": { @@ -36,6 +36,15 @@ "compilerOptions": { "tsConfigPath": "libs/providers/tsconfig.lib.json" } + }, + "dtos": { + "type": "library", + "root": "libs/dtos", + "entryFile": "index", + "sourceRoot": "libs/dtos/src", + "compilerOptions": { + "tsConfigPath": "libs/dtos/tsconfig.lib.json" + } } } } diff --git a/package.json b/package.json index a31e480..e7377eb 100644 --- a/package.json +++ b/package.json @@ -1,99 +1,100 @@ { - "name": "zkchainHub", - "version": "0.0.1", - "description": "", - "author": "", - "private": true, - "license": "UNLICENSED", - "scripts": { - "build": "nest build", - "format": "prettier --write \"{apps,libs}/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/apps/api/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./apps/api/test/jest-e2e.json", - "prepare": "husky", - "preinstall": "npx only-allow pnpm" + "name": "zkchainHub", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"{apps,libs}/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/apps/api/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./apps/api/test/jest-e2e.json", + "prepare": "husky", + "preinstall": "npx only-allow pnpm" + }, + "dependencies": { + "@nestjs/common": "10.0.0", + "@nestjs/core": "10.0.0", + "@nestjs/platform-express": "10.0.0", + "@nestjs/swagger": "7.4.0", + "reflect-metadata": "0.1.13", + "rxjs": "7.8.1" + }, + "devDependencies": { + "@commitlint/config-conventional": "19.2.2", + "@golevelup/ts-jest": "0.5.0", + "@ianvs/prettier-plugin-sort-imports": "4.3.0", + "@nestjs/cli": "10.0.0", + "@nestjs/schematics": "10.0.0", + "@nestjs/testing": "10.0.0", + "@total-typescript/tsconfig": "1.0.4", + "@types/express": "4.17.17", + "@types/jest": "29.5.2", + "@types/node": "20.3.1", + "@types/supertest": "6.0.0", + "@typescript-eslint/eslint-plugin": "7.0.0", + "@typescript-eslint/parser": "7.0.0", + "commitlint": "19.3.0", + "eslint": "8.42.0", + "eslint-config-prettier": "9.0.0", + "eslint-plugin-prettier": "5.0.0", + "husky": "9.0.11", + "jest": "29.5.0", + "lint-staged": "15.2.7", + "prettier": "3.0.0", + "source-map-support": "0.5.21", + "supertest": "7.0.0", + "ts-jest": "29.1.0", + "ts-loader": "9.4.3", + "ts-node": "10.9.1", + "tsconfig-paths": "4.2.0", + "typescript": "5.1.3" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": ".", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + ".+\\.(t|j)s$": "ts-jest" }, - "dependencies": { - "@nestjs/common": "10.0.0", - "@nestjs/core": "10.0.0", - "@nestjs/platform-express": "10.0.0", - "@nestjs/swagger": "7.4.0", - "reflect-metadata": "0.1.13", - "rxjs": "7.8.1" - }, - "devDependencies": { - "@commitlint/config-conventional": "19.2.2", - "@golevelup/ts-jest": "0.5.0", - "@ianvs/prettier-plugin-sort-imports": "4.3.0", - "@nestjs/cli": "10.0.0", - "@nestjs/schematics": "10.0.0", - "@nestjs/testing": "10.0.0", - "@total-typescript/tsconfig": "1.0.4", - "@types/express": "4.17.17", - "@types/jest": "29.5.2", - "@types/node": "20.3.1", - "@types/supertest": "6.0.0", - "@typescript-eslint/eslint-plugin": "7.0.0", - "@typescript-eslint/parser": "7.0.0", - "commitlint": "19.3.0", - "eslint": "8.42.0", - "eslint-config-prettier": "9.0.0", - "eslint-plugin-prettier": "5.0.0", - "husky": "9.0.11", - "jest": "29.5.0", - "lint-staged": "15.2.7", - "prettier": "3.0.0", - "source-map-support": "0.5.21", - "supertest": "7.0.0", - "ts-jest": "29.1.0", - "ts-loader": "9.4.3", - "ts-node": "10.9.1", - "tsconfig-paths": "4.2.0", - "typescript": "5.1.3" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": ".", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - ".+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "./coverage", - "coveragePathIgnorePatterns": [ - "/node_modules/", - ".e2e-spec.ts", - ".module.ts", - "main.ts" - ], - "testEnvironment": "node", - "roots": [ - "/apps/", - "/libs/" - ], - "moduleNameMapper": { - "^@packages/providers(|/.*)$": "/libs/providers/src/$1" - } - }, - "packageManager": "pnpm@9.5.0+sha1.8c155dc114e1689d18937974f6571e0ceee66f1d", - "lint-staged": { - "(apps|libs)/**/*.(ts|js)": [ - "pnpm lint", - "pnpm format" - ] + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "./coverage", + "coveragePathIgnorePatterns": [ + "/node_modules/", + ".e2e-spec.ts", + ".module.ts", + "main.ts" + ], + "testEnvironment": "node", + "roots": [ + "/apps/", + "/libs/" + ], + "moduleNameMapper": { + "^@packages/providers(|/.*)$": "/libs/providers/src/$1", + "^@shared/dtos(|/.*)$": "/libs/dtos/src/$1" } -} + }, + "packageManager": "pnpm@9.5.0+sha1.8c155dc114e1689d18937974f6571e0ceee66f1d", + "lint-staged": { + "(apps|libs)/**/*.(ts|js)": [ + "pnpm lint", + "pnpm format" + ] + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 1196eab..3fa7db0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,37 @@ { - "extends": "@total-typescript/tsconfig/tsc/no-dom/app", - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": true, - "noImplicitAny": true, - "strictBindCallApply": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "paths": { - "@packages/providers": ["libs/providers/src"], - "@packages/providers/*": ["libs/providers/src/*"] - }, - "verbatimModuleSyntax": false - } -} + "extends": "@total-typescript/tsconfig/tsc/no-dom/app", + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "paths": { + "@packages/providers": [ + "libs/providers/src" + ], + "@packages/providers/*": [ + "libs/providers/src/*" + ], + "@shared/dtos": [ + "libs/dtos/src" + ], + "@shared/dtos/*": [ + "libs/dtos/src/*" + ] + }, + "verbatimModuleSyntax": false + } +} \ No newline at end of file