From 19d67b975e37973b89983c38d1c43189fbf215ae Mon Sep 17 00:00:00 2001 From: osman sonmezturk Date: Wed, 14 Jun 2023 15:02:55 +0300 Subject: [PATCH 1/4] draft for rest api --- package.json | 2 +- proto/defi-providers.proto | 79 ---------------------------------- src/app.controller.ts | 32 +++++++------- src/app.service.ts | 11 ++--- src/factory/factory.service.ts | 53 ++++++++--------------- src/generic.interceptor.ts | 4 +- src/genericRpcError.ts | 13 +++--- src/main.ts | 20 +-------- 8 files changed, 49 insertions(+), 165 deletions(-) delete mode 100644 proto/defi-providers.proto diff --git a/package.json b/package.json index f46253d5..eac9ad79 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "prebuild": "rimraf dist", "dev": "APP_ENV=dev ts-node src/main.ts", "prod": "APP_ENV=prod node dist/main", - "build": "rm -rf src/generated && mkdir src/generated && protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=esModuleInterop=true,nestJs=true,addNestjsRestParameter=true --ts_proto_out=src/generated proto/*.proto && tsc -p tsconfig.build.json", + "build": "rm -rf src/generated && mkdir src/generated && tsc -p tsconfig.build.json", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "nest start", "start:dev": "nest start --watch", diff --git a/proto/defi-providers.proto b/proto/defi-providers.proto deleted file mode 100644 index 2c1af5b8..00000000 --- a/proto/defi-providers.proto +++ /dev/null @@ -1,79 +0,0 @@ -syntax = "proto3"; - -package dappradar.defi.providers; - -service DefiProviders { - rpc GetTvl (GetTvlRequest) returns (GetTvlReply); - rpc GetPoolAndTokenVolumes (GetPoolAndTokenVolumesRequest) returns (GetPoolAndTokenVolumesReply); - rpc GetTokenDetails (GetTokenDetailsRequest) returns (GetTokenDetailsReply); - rpc HealthCheck (HealthCheckRequest) returns (HealthCheckReply); -} - -message HealthCheckRequest { -} - -message HealthCheckReply { - bool run = 1; -} - -message GetTokenDetailsRequest { - string provider = 1; - string chain = 2; -} - -message GetTokenDetailsReply { - string address = 1; - string name = 2; - string symbol = 3; - uint32 decimals = 4; - string logo = 5; -} - -message GetTvlRequest { - string provider = 1; - string chain = 2; - GetTvlQuery query = 3; -} - -message GetTvlQuery { - string block = 1; - string date = 2; -} - -message GetTvlReply { - map balances = 1; - map poolBalances = 2; -} - -message PoolBalance { - repeated string tokens = 1; - repeated string balances = 2; -} - - -message GetPoolAndTokenVolumesRequest { - string provider = 1; - string chain = 2; - GetPoolAndTokenVolumesQuery query = 3; -} - -message GetPoolAndTokenVolumesQuery { - string block = 1; - repeated string pools = 2; - repeated string tokens = 3; -} - -message GetPoolAndTokenVolumesReply { - map poolVolumes = 1; - map tokenVolumes = 2; -} - -message PoolVolume { - repeated string volumes = 1; - string volumeUsd = 2; -} - -message TokenVolume { - string volume = 1; - string volumeUsd = 2; -} \ No newline at end of file diff --git a/src/app.controller.ts b/src/app.controller.ts index 13ea09b4..66ff9f39 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,44 +1,42 @@ -import { Controller, UseFilters } from '@nestjs/common'; +import { Controller, Get, Query, UseFilters } from '@nestjs/common'; import { AppService } from './app.service'; -import { GrpcMethod } from '@nestjs/microservices'; +import { GenericErrorFilter } from './genericRpcError'; import { - GetTvlRequest, - GetTvlReply, - GetPoolAndTokenVolumesRequest, GetPoolAndTokenVolumesReply, + GetPoolAndTokenVolumesRequest, GetTokenDetailsReply, GetTokenDetailsRequest, - HealthCheckRequest, + GetTvlReply, + GetTvlRequest, HealthCheckReply, -} from './generated/proto/defi-providers'; -import { GenericRpcErrorFilter } from './genericRpcError'; +} from './interfaces/IController'; @Controller() -@UseFilters(new GenericRpcErrorFilter()) +@UseFilters(new GenericErrorFilter()) export class AppController { constructor(private readonly appService: AppService) {} - @GrpcMethod('DefiProviders', 'GetTvl') - async getTvl(req: GetTvlRequest): Promise { + @Get('tvl') + async getTvl(@Query() req: GetTvlRequest): Promise { return await this.appService.getTvl(req); } - @GrpcMethod('DefiProviders', 'GetPoolAndTokenVolumes') + @Get('pool-token-volumes') async getPoolAndTokenVolumes( - req: GetPoolAndTokenVolumesRequest, + @Query() req: GetPoolAndTokenVolumesRequest, ): Promise { return await this.appService.getPoolAndTokenVolumes(req); } - @GrpcMethod('DefiProviders', 'GetTokenDetails') + @Get('token-detail') async getTokenDetails( - req: GetTokenDetailsRequest, + @Query() req: GetTokenDetailsRequest, ): Promise { return await this.appService.getTokenDetails(req); } - @GrpcMethod('DefiProviders', 'HealthCheck') - async heathCheck(req: HealthCheckRequest): Promise { + @Get('health-check') + async heathCheck(req): Promise { return { run: true }; } } diff --git a/src/app.service.ts b/src/app.service.ts index ba5ed1d6..c4305397 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@nestjs/common'; import { - GetTvlRequest, - GetTvlReply, - GetPoolAndTokenVolumesRequest, GetPoolAndTokenVolumesReply, - GetTokenDetailsRequest, + GetPoolAndTokenVolumesRequest, GetTokenDetailsReply, -} from './generated/proto/defi-providers'; + GetTokenDetailsRequest, + GetTvlReply, + GetTvlRequest, +} from './interfaces/IController'; + import { FactoryService } from './factory/factory.service'; @Injectable() diff --git a/src/factory/factory.service.ts b/src/factory/factory.service.ts index 31dd7e9f..84a029fd 100644 --- a/src/factory/factory.service.ts +++ b/src/factory/factory.service.ts @@ -1,15 +1,16 @@ import { Injectable } from '@nestjs/common'; import BigNumber from 'bignumber.js'; import { - GetTvlRequest, - GetTvlReply, - GetPoolAndTokenVolumesRequest, GetPoolAndTokenVolumesReply, + GetPoolAndTokenVolumesRequest, + GetTokenDetailsReply, + GetTokenDetailsRequest, + GetTvlReply, + GetTvlRequest, PoolVolume, TokenVolume, - GetTokenDetailsRequest, - GetTokenDetailsReply, -} from '../generated/proto/defi-providers'; +} from '../interfaces/IController'; + import { RpcException } from '@nestjs/microservices'; import { Web3ProviderService } from '../web3Provider/web3Provider.service'; import { log } from '../util/logger/logger'; @@ -32,7 +33,7 @@ export class FactoryService { req: GetTvlRequest, timeoutErrorCount = 0, ): Promise { - if (req.query.block === undefined) { + if (req.block === undefined) { throw new RpcException('Block is undefined'); } if (this.web3ProviderService.checkNodeUrl(req?.chain)) { @@ -41,36 +42,16 @@ export class FactoryService { const providerService: IProvider = await import( this.getProviderServicePath(req.chain, req.provider, 'index') ); - try { - const tvlData = await providerService.tvl({ - web3: await this.web3ProviderService.getWeb3(req?.chain), - chain: req?.chain, - provider: req?.provider, - block: parseInt(req.query?.block), - date: req.query?.date, - }); + const tvlData = await providerService.tvl({ + web3: await this.web3ProviderService.getWeb3(req?.chain), + chain: req?.chain, + provider: req?.provider, + block: parseInt(req?.block), + date: req?.date, + }); - const balances = basicUtil.checkZeroBalance(tvlData.balances); - return { balances, poolBalances: tvlData.poolBalances }; - } catch (err) { - log.error({ - message: err?.message || '', - stack: err?.stack || '', - detail: `Error: chain: ${req.chain}, provider: ${req?.provider}, blocknumber: ${req.query?.block}, `, - endpoint: 'getTvl', - }); - if (err?.message?.toLowerCase() == 'timeout' && timeoutErrorCount < 3) { - log.error({ - message: err?.message || '', - stack: err?.stack || '', - detail: `Error: web3Instance changed for chain: ${req.chain}, provider: ${req?.provider}, blocknumber: ${req.query?.block} `, - endpoint: 'getTvl', - }); - timeoutErrorCount++; - await this.web3ProviderService.changeInstance(req?.chain); - return this.getTvl(req, timeoutErrorCount); - } - } + const balances = basicUtil.checkZeroBalance(tvlData.balances); + return { balances, poolBalances: tvlData.poolBalances }; } async getPoolAndTokenVolumes( diff --git a/src/generic.interceptor.ts b/src/generic.interceptor.ts index 5bc85e56..06ad4b81 100644 --- a/src/generic.interceptor.ts +++ b/src/generic.interceptor.ts @@ -17,7 +17,9 @@ export class GenericInterceptor implements NestInterceptor { const endpoint = context.getHandler(); log.info({ message: - endpoint.name == 'getPoolAndTokenVolumes' ? '' : JSON.stringify(req), + endpoint.name == 'getPoolAndTokenVolumes' + ? '' + : JSON.stringify(req.query), endpoint: endpoint.name, }); return next.handle().pipe(); diff --git a/src/genericRpcError.ts b/src/genericRpcError.ts index 3d272069..3e33c225 100644 --- a/src/genericRpcError.ts +++ b/src/genericRpcError.ts @@ -3,30 +3,27 @@ import { Observable, throwError } from 'rxjs'; import { ArgumentsHost, Catch, + ExceptionFilter, HttpStatus, - RpcExceptionFilter, } from '@nestjs/common'; -import { RpcException } from '@nestjs/microservices'; @Catch() -export class GenericRpcErrorFilter implements RpcExceptionFilter { +export class GenericErrorFilter implements ExceptionFilter { catch(exception: any, host: ArgumentsHost): Observable { - const ctx = host.switchToRpc(); + const ctx = host.switchToHttp(); const errorResponse: any = { statusCode: HttpStatus.INTERNAL_SERVER_ERROR, timestamp: new Date().toISOString(), errorName: exception?.name, message: exception?.message, - requestData: ctx.getData(), + requestData: JSON.stringify(ctx.getRequest().query), }; log.error({ message: errorResponse?.message, stack: exception?.stack || '', detail: errorResponse?.errorResponsestatusCode, - endpoint: `path: ${host.getArgs()[2]?.call?.handler?.path} chain: ${ - ctx.getData()?.chain - } provider: ${ctx.getData()?.provider}`, + endpoint: ctx.getRequest().url, }); return throwError(() => errorResponse); } diff --git a/src/main.ts b/src/main.ts index 813ceb5c..cb70680b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,28 +1,12 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { config } from './app.config'; -import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { log } from './util/logger/logger'; async function bootstrap() { const URI = `${config.HOST}:${config.PORT}`; - const app = await NestFactory.createMicroservice( - AppModule, - { - transport: Transport.GRPC, - options: { - url: URI, - package: config.DEFI_PROVIDERS_SERVICE_PACKAGE, - protoPath: config.DEFI_PROVIDERS_SERVICE_PROTOFILE, - channelOptions: { - 'grpc.max_send_message_length': 1024 * 1024 * 1024, - 'grpc.max_receive_message_length': 1024 * 1024 * 1024, - 'grpc.max_concurrent_streams': 10, - }, - }, - }, - ); - await app.listen(); + const app = await NestFactory.create(AppModule); + await app.listen(config.PORT); log.info({ message: `Microservice is listening on ${URI}`, endpoint: 'bootstrap', From a1932351d086a7f5b92d7473c1ab477bda8c769d Mon Sep 17 00:00:00 2001 From: osman sonmezturk Date: Tue, 4 Jul 2023 16:39:54 +0300 Subject: [PATCH 2/4] add missing interface class --- src/interfaces/IController.ts | 80 +++++++++++++++++++++++++++++++++++ src/main.ts | 2 +- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/interfaces/IController.ts diff --git a/src/interfaces/IController.ts b/src/interfaces/IController.ts new file mode 100644 index 00000000..af570b14 --- /dev/null +++ b/src/interfaces/IController.ts @@ -0,0 +1,80 @@ +export interface HealthCheckReply { + run: boolean; +} + +export interface GetTokenDetailsRequest { + provider: string; + chain: string; +} + +export interface GetTokenDetailsReply { + address: string; + name: string; + symbol: string; + decimals: number; + logo: string; +} + +export interface GetTvlRequest { + provider: string; + chain: string; + block: string; + date: string; +} + +export interface GetTvlReply { + balances: { [key: string]: string }; + poolBalances: { [key: string]: PoolBalance }; +} + +export interface GetTvlReply_BalancesEntry { + key: string; + value: string; +} + +export interface GetTvlReply_PoolBalancesEntry { + key: string; + value: PoolBalance | undefined; +} + +export interface PoolBalance { + tokens: string[]; + balances: string[]; +} + +export interface GetPoolAndTokenVolumesRequest { + provider: string; + chain: string; + query: GetPoolAndTokenVolumesQuery | undefined; +} + +export interface GetPoolAndTokenVolumesQuery { + block: string; + pools: string[]; + tokens: string[]; +} + +export interface GetPoolAndTokenVolumesReply { + poolVolumes: { [key: string]: PoolVolume }; + tokenVolumes: { [key: string]: TokenVolume }; +} + +export interface GetPoolAndTokenVolumesReply_PoolVolumesEntry { + key: string; + value: PoolVolume | undefined; +} + +export interface GetPoolAndTokenVolumesReply_TokenVolumesEntry { + key: string; + value: TokenVolume | undefined; +} + +export interface PoolVolume { + volumes: string[]; + volumeUsd: string; +} + +export interface TokenVolume { + volume: string; + volumeUsd: string; +} diff --git a/src/main.ts b/src/main.ts index cb70680b..3ee1790d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,7 @@ async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(config.PORT); log.info({ - message: `Microservice is listening on ${URI}`, + message: `Defi Provider is listening on ${URI}`, endpoint: 'bootstrap', }); } From 28529d8676bab789096e62a73c22754f41abb0f1 Mon Sep 17 00:00:00 2001 From: osman sonmezturk Date: Tue, 4 Jul 2023 16:49:15 +0300 Subject: [PATCH 3/4] name convention was changed --- src/app.controller.ts | 2 +- src/{genericRpcError.ts => genericErrorFilter.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{genericRpcError.ts => genericErrorFilter.ts} (100%) diff --git a/src/app.controller.ts b/src/app.controller.ts index 66ff9f39..5d0eb82e 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Query, UseFilters } from '@nestjs/common'; import { AppService } from './app.service'; -import { GenericErrorFilter } from './genericRpcError'; +import { GenericErrorFilter } from './genericErrorFilter'; import { GetPoolAndTokenVolumesReply, GetPoolAndTokenVolumesRequest, diff --git a/src/genericRpcError.ts b/src/genericErrorFilter.ts similarity index 100% rename from src/genericRpcError.ts rename to src/genericErrorFilter.ts From 127d892644364f7461032394abe3df661d4113e0 Mon Sep 17 00:00:00 2001 From: osman sonmezturk Date: Tue, 4 Jul 2023 18:47:01 +0300 Subject: [PATCH 4/4] get end-point was changed to post method --- src/app.controller.ts | 15 +++------------ src/app.module.ts | 3 +-- src/app.service.ts | 6 ------ src/factory/factory.service.ts | 19 ++++--------------- src/health/health.controller.ts | 24 ------------------------ src/interfaces/IController.ts | 4 ---- 6 files changed, 8 insertions(+), 63 deletions(-) delete mode 100644 src/health/health.controller.ts diff --git a/src/app.controller.ts b/src/app.controller.ts index 5d0eb82e..a757aedf 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,11 +1,9 @@ -import { Controller, Get, Query, UseFilters } from '@nestjs/common'; +import { Body, Controller, Get, Post, Query, UseFilters } from '@nestjs/common'; import { AppService } from './app.service'; import { GenericErrorFilter } from './genericErrorFilter'; import { GetPoolAndTokenVolumesReply, GetPoolAndTokenVolumesRequest, - GetTokenDetailsReply, - GetTokenDetailsRequest, GetTvlReply, GetTvlRequest, HealthCheckReply, @@ -21,20 +19,13 @@ export class AppController { return await this.appService.getTvl(req); } - @Get('pool-token-volumes') + @Post('pool-token-volumes') async getPoolAndTokenVolumes( - @Query() req: GetPoolAndTokenVolumesRequest, + @Body() req: GetPoolAndTokenVolumesRequest, ): Promise { return await this.appService.getPoolAndTokenVolumes(req); } - @Get('token-detail') - async getTokenDetails( - @Query() req: GetTokenDetailsRequest, - ): Promise { - return await this.appService.getTokenDetails(req); - } - @Get('health-check') async heathCheck(req): Promise { return { run: true }; diff --git a/src/app.module.ts b/src/app.module.ts index 180b877b..1c91c2fc 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,11 +4,10 @@ import { AppController } from './app.controller'; import { AppService } from './app.service'; import { FactoryModule } from './factory/factory.module'; import { GenericInterceptor } from './generic.interceptor'; -import { HealthController } from './health/health.controller'; @Module({ imports: [FactoryModule], - controllers: [AppController, HealthController], + controllers: [AppController], providers: [ AppService, { diff --git a/src/app.service.ts b/src/app.service.ts index c4305397..688bfa41 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -23,10 +23,4 @@ export class AppService { ): Promise { return await this.factoryService.getPoolAndTokenVolumes(req); } - - async getTokenDetails( - req: GetTokenDetailsRequest, - ): Promise { - return await this.factoryService.getTokenDetails(req); - } } diff --git a/src/factory/factory.service.ts b/src/factory/factory.service.ts index d794f77c..c28ef224 100644 --- a/src/factory/factory.service.ts +++ b/src/factory/factory.service.ts @@ -3,8 +3,6 @@ import BigNumber from 'bignumber.js'; import { GetPoolAndTokenVolumesReply, GetPoolAndTokenVolumesRequest, - GetTokenDetailsReply, - GetTokenDetailsRequest, GetTvlReply, GetTvlRequest, PoolVolume, @@ -58,7 +56,7 @@ export class FactoryService { async getPoolAndTokenVolumes( req: GetPoolAndTokenVolumesRequest, ): Promise { - if (req.query.block === undefined) { + if (req.block === undefined) { throw new RpcException('Block is undefined'); } @@ -66,13 +64,13 @@ export class FactoryService { this.getProviderServicePath(req.chain, req.provider, 'index') ); - const block = parseInt(req.query.block) - basicUtil.getDelay(req.chain); + const block = parseInt(req.block) - basicUtil.getDelay(req.chain); const poolVolumes = await providerService.getPoolVolumes({ chain: req.chain, provider: req.provider, block, - pools: req.query.pools, + pools: req.pools, }); for (const [, poolVolume] of Object.entries(poolVolumes)) { poolVolume.volumes = poolVolume.volumes.map((volume) => @@ -85,7 +83,7 @@ export class FactoryService { chain: req.chain, provider: req.provider, block, - tokens: req.query.tokens, + tokens: req.tokens, }); for (const [, tokenVolume] of Object.entries(tokenVolumes)) { tokenVolume.volume = BigNumber(tokenVolume.volume).toFixed(); @@ -95,15 +93,6 @@ export class FactoryService { return { poolVolumes, tokenVolumes }; } - async getTokenDetails( - req: GetTokenDetailsRequest, - ): Promise { - const { address, name, symbol, decimals, logo } = await import( - this.getProviderServicePath(req.chain, req.provider, 'data.json') - ); - return { address, name, symbol, decimals, logo }; - } - getProviderServicePath( chain: string, provider: string, diff --git a/src/health/health.controller.ts b/src/health/health.controller.ts deleted file mode 100644 index f78b88d3..00000000 --- a/src/health/health.controller.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { GrpcMethod } from '@nestjs/microservices'; -import { Controller } from '@nestjs/common'; - -enum ServingStatus { - UNKNOWN = 0, - SERVING = 1, - NOT_SERVING = 2, -} - -interface HealthCheckRequest { - service: string; -} - -interface HealthCheckResposne { - status: ServingStatus; -} - -@Controller() -export class HealthController { - @GrpcMethod('DefiProviders', 'Check') - check(data: HealthCheckRequest, metadata: any): HealthCheckResposne { - return { status: ServingStatus.SERVING }; - } -} diff --git a/src/interfaces/IController.ts b/src/interfaces/IController.ts index af570b14..472224f4 100644 --- a/src/interfaces/IController.ts +++ b/src/interfaces/IController.ts @@ -45,10 +45,6 @@ export interface PoolBalance { export interface GetPoolAndTokenVolumesRequest { provider: string; chain: string; - query: GetPoolAndTokenVolumesQuery | undefined; -} - -export interface GetPoolAndTokenVolumesQuery { block: string; pools: string[]; tokens: string[];