diff --git a/api/.eslintrc.js b/api/.eslintrc.js index 259de13..1841b8b 100644 --- a/api/.eslintrc.js +++ b/api/.eslintrc.js @@ -6,10 +6,7 @@ module.exports = { sourceType: 'module', }, plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], + extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], root: true, env: { node: true, diff --git a/api/.prettierignore b/api/.prettierignore new file mode 100644 index 0000000..2a3845f --- /dev/null +++ b/api/.prettierignore @@ -0,0 +1 @@ +transformer/ diff --git a/api/.prettierrc b/api/.prettierrc deleted file mode 100644 index 0081bf7..0000000 --- a/api/.prettierrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "trailingComma": "all" -} \ No newline at end of file diff --git a/api/README.md b/api/README.md index 0ae44d8..4b97217 100644 --- a/api/README.md +++ b/api/README.md @@ -34,7 +34,6 @@ Before you begin, ensure you have met the following requirements: - npm or yarn - Redis - ## Installation First, install the project dependencies: @@ -42,11 +41,12 @@ First, install the project dependencies: ```bash $ npm install ``` -Then, install [Redis](https://redis.io/docs/install/install-redis/) on your machine. + +Then, install [Redis](https://redis.io/docs/install/install-redis/) on your machine. After installing Redis, start the Redis server: -```bash +```bash $ brew services start redis ``` diff --git a/api/index.js b/api/index.js index cf468b4..fb0e3ff 100644 --- a/api/index.js +++ b/api/index.js @@ -1,5 +1,5 @@ import BN from 'bn.js'; -const v = '0x86ea91e266287d11011f5c0b3963d2ea' +const v = '0x86ea91e266287d11011f5c0b3963d2ea'; const b = new BN(v.substring(2), 'hex'); console.log(b.toString(10)); diff --git a/api/jest.config.ts b/api/jest.config.ts index 59e859d..b191768 100644 --- a/api/jest.config.ts +++ b/api/jest.config.ts @@ -1,12 +1,12 @@ import type { JestConfigWithTsJest } from 'ts-jest'; const jestConfig: JestConfigWithTsJest = { - moduleFileExtensions: ["js", "json", "ts"], - rootDir: "src", - testRegex: ".*\\.spec\\.ts$", - collectCoverageFrom: ["**/*.(t|j)s"], - coverageDirectory: "../coverage", - testEnvironment: "node", + moduleFileExtensions: ['js', 'json', 'ts'], + rootDir: 'src', + testRegex: '.*\\.spec\\.ts$', + collectCoverageFrom: ['**/*.(t|j)s'], + coverageDirectory: '../coverage', + testEnvironment: 'node', preset: 'ts-jest/presets/default-esm', // or other ESM presets moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', diff --git a/api/src/app/app.module.ts b/api/src/app/app.module.ts index 6b779d8..74661f8 100644 --- a/api/src/app/app.module.ts +++ b/api/src/app/app.module.ts @@ -1,21 +1,21 @@ -import { join } from "node:path"; -import { Module } from "@nestjs/common"; -import { GraphQLModule as NestGraphQLModule } from "@nestjs/graphql"; -import { ConfigModule } from "@nestjs/config"; -import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo"; +import { join } from 'node:path'; +import { Module } from '@nestjs/common'; +import { GraphQLModule as NestGraphQLModule } from '@nestjs/graphql'; +import { ConfigModule } from '@nestjs/config'; +import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; -import config from "../config/config.js"; -import { AppService } from "./app.service.js"; -import { ClickhouseModule } from "../clickhouse/clickhouse.module.js"; -import { OlModule } from "../ol/ol.module.js"; -import { S3Module } from "../s3/s3.module.js"; -import { NodeWatcherModule } from "../node-watcher/node-watcher.module.js"; -import { GraphQLModule } from "../graphql/graphql.module.js"; -import { StatsModule } from "../stats/stats.module.js"; -import { WalletSubscriptionModule } from "../wallet-subscription/wallet-subscription.module.js"; -import { NatsModule } from "../nats/nats.module.js"; -import { OlSwapModule } from "../ol-swap/OlSwapModule.js"; -import { MultiSigModule } from "../multi-sig/multi-sig.module.js"; +import config from '../config/config.js'; +import { AppService } from './app.service.js'; +import { ClickhouseModule } from '../clickhouse/clickhouse.module.js'; +import { OlModule } from '../ol/ol.module.js'; +import { S3Module } from '../s3/s3.module.js'; +import { NodeWatcherModule } from '../node-watcher/node-watcher.module.js'; +import { GraphQLModule } from '../graphql/graphql.module.js'; +import { StatsModule } from '../stats/stats.module.js'; +import { WalletSubscriptionModule } from '../wallet-subscription/wallet-subscription.module.js'; +import { NatsModule } from '../nats/nats.module.js'; +import { OlSwapModule } from '../ol-swap/OlSwapModule.js'; +import { MultiSigModule } from '../multi-sig/multi-sig.module.js'; @Module({ imports: [ @@ -29,7 +29,7 @@ import { MultiSigModule } from "../multi-sig/multi-sig.module.js"; NestGraphQLModule.forRoot({ driver: ApolloDriver, - autoSchemaFile: join(process.cwd(), "src/schema.gql"), + autoSchemaFile: join(process.cwd(), 'src/schema.gql'), playground: true, subscriptions: { 'graphql-ws': { diff --git a/api/src/app/app.service.ts b/api/src/app/app.service.ts index 7c905cd..5fa7f61 100644 --- a/api/src/app/app.service.ts +++ b/api/src/app/app.service.ts @@ -17,15 +17,15 @@ export class AppService implements OnModuleInit { const addr = server.address(); if (addr && typeof addr !== 'string') { - let host: string; - if (addr.family === 'IPv6') { - host = `[${addr.address}]:${addr.port}`; - } else { - host = `${addr.address}:${addr.port}`; - } + let host: string; + if (addr.family === 'IPv6') { + host = `[${addr.address}]:${addr.port}`; + } else { + host = `${addr.address}:${addr.port}`; + } - const uri = `http://${host}`; - this.logger.log(`Serving at ${uri}"`); + const uri = `http://${host}`; + this.logger.log(`Serving at ${uri}"`); } } } diff --git a/api/src/clickhouse/clickhouse.module.ts b/api/src/clickhouse/clickhouse.module.ts index d793311..ed83f67 100644 --- a/api/src/clickhouse/clickhouse.module.ts +++ b/api/src/clickhouse/clickhouse.module.ts @@ -3,6 +3,6 @@ import { ClickhouseService } from './clickhouse.service.js'; @Module({ providers: [ClickhouseService], - exports: [ClickhouseService] + exports: [ClickhouseService], }) export class ClickhouseModule {} diff --git a/api/src/clickhouse/clickhouse.service.ts b/api/src/clickhouse/clickhouse.service.ts index 0e2242a..9e245c6 100644 --- a/api/src/clickhouse/clickhouse.service.ts +++ b/api/src/clickhouse/clickhouse.service.ts @@ -1,17 +1,13 @@ -import pathUtil from "node:path"; -import fs from "node:fs"; +import pathUtil from 'node:path'; +import fs from 'node:fs'; -import axios from "axios"; -import qs from "qs"; -import { - Injectable, - OnApplicationShutdown, - OnModuleInit, -} from "@nestjs/common"; -import { createClient, ClickHouseClient } from "@clickhouse/client"; -import { ConfigService } from "@nestjs/config"; +import axios from 'axios'; +import qs from 'qs'; +import { Injectable, OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; +import { createClient, ClickHouseClient } from '@clickhouse/client'; +import { ConfigService } from '@nestjs/config'; -import { ClickhouseConfig } from "../config/config.interface.js"; +import { ClickhouseConfig } from '../config/config.interface.js'; export interface ClickhouseQueryResponse { meta: { name: string; type: string }[]; @@ -29,13 +25,13 @@ export class ClickhouseService implements OnModuleInit, OnApplicationShutdown { private readonly config: ClickhouseConfig; public constructor(config: ConfigService) { - const clickhouseConfig = config.get("clickhouse")!; + const clickhouseConfig = config.get('clickhouse')!; this.config = clickhouseConfig; - let protocol = "http"; + let protocol = 'http'; if (clickhouseConfig.port === 443) { - protocol = "https"; + protocol = 'https'; } const url = `${protocol}://${clickhouseConfig.host}:${clickhouseConfig.port}`; @@ -50,13 +46,13 @@ export class ClickhouseService implements OnModuleInit, OnApplicationShutdown { public async onModuleInit() { const dirname = pathUtil.dirname(new URL(import.meta.url).pathname); - const queriesDir = pathUtil.join(dirname, "queries"); + const queriesDir = pathUtil.join(dirname, 'queries'); const files = await fs.promises.readdir(queriesDir); for (const file of files) { - const queryName = file.split(".")[0]; + const queryName = file.split('.')[0]; this.insertQueries.set( queryName, - await fs.promises.readFile(pathUtil.join(queriesDir, file), "utf-8"), + await fs.promises.readFile(pathUtil.join(queriesDir, file), 'utf-8'), ); } } @@ -66,7 +62,7 @@ export class ClickhouseService implements OnModuleInit, OnApplicationShutdown { } public async insertParquetFile(path: string): Promise { - const queryName = pathUtil.basename(path).split(".")[0]; + const queryName = pathUtil.basename(path).split('.')[0]; const query = this.insertQueries.get(queryName); if (!query) { throw new Error(`insert query missing for ${queryName}`); @@ -74,20 +70,18 @@ export class ClickhouseService implements OnModuleInit, OnApplicationShutdown { const clickhouseConfig = this.config; - let protocol = "http"; + let protocol = 'http'; if (clickhouseConfig.port === 443) { - protocol = "https"; + protocol = 'https'; } - const url = `${protocol}://${clickhouseConfig.host}:${clickhouseConfig.port}/?${qs.stringify( - { - database: clickhouseConfig.database, - query, - }, - )}`; + const url = `${protocol}://${clickhouseConfig.host}:${clickhouseConfig.port}/?${qs.stringify({ + database: clickhouseConfig.database, + query, + })}`; await axios({ - method: "POST", + method: 'POST', url, auth: { username: clickhouseConfig.username, diff --git a/api/src/config/config.interface.ts b/api/src/config/config.interface.ts index d842514..2ad8d89 100644 --- a/api/src/config/config.interface.ts +++ b/api/src/config/config.interface.ts @@ -52,4 +52,4 @@ export interface FirebaseConfig { export interface NatsConfig { servers: string; -} \ No newline at end of file +} diff --git a/api/src/config/config.ts b/api/src/config/config.ts index 177bff4..c2cceed 100644 --- a/api/src/config/config.ts +++ b/api/src/config/config.ts @@ -6,7 +6,7 @@ export default (): Config => { const config: Config = { port: ENV.PORT !== undefined ? parseInt(ENV.PORT, 10) : 3000, - cacheEnabled: ENV.CACHE_ENABLED === "true", + cacheEnabled: ENV.CACHE_ENABLED === 'true', dataApiHost: ENV.DATA_API_HOST!, info: { @@ -14,7 +14,7 @@ export default (): Config => { }, ol: { - provider: "https://rpc.0l.fyi", + provider: 'https://rpc.0l.fyi', dataApiHost: ENV.DATA_API_HOST!, }, @@ -24,7 +24,7 @@ export default (): Config => { accessKey: ENV.S3_ACCESS_KEY_ID!, secretKey: ENV.S3_SECRET_ACCESS_KEY!, port: ENV.S3_PORT ? parseInt(ENV.S3_PORT, 10) : 443, - useSSL: ENV.S3_USE_SSL ? ENV.S3_USE_SSL === "true" : true, + useSSL: ENV.S3_USE_SSL ? ENV.S3_USE_SSL === 'true' : true, bucket: ENV.S3_BUCKET!, storageClass: ENV.S3_STORAGE_CLASS!, }, @@ -39,7 +39,7 @@ export default (): Config => { apn: ENV.APN_PRIVATE_KEY ? { - privateKey: Buffer.from(ENV.APN_PRIVATE_KEY, "base64"), + privateKey: Buffer.from(ENV.APN_PRIVATE_KEY, 'base64'), keyId: ENV.APN_KEY_ID!, teamId: ENV.APN_TEAM_ID!, } @@ -47,12 +47,13 @@ export default (): Config => { firebase: ENV.FIREBASE_SERVICE_ACCOUNT ? { - serviceAccount: ENV.FIREBASE_SERVICE_ACCOUNT, - } : undefined, + serviceAccount: ENV.FIREBASE_SERVICE_ACCOUNT, + } + : undefined, nats: { servers: ENV.NATS_SERVERS!, - } + }, }; return config; diff --git a/api/src/firebase/firebase.module.ts b/api/src/firebase/firebase.module.ts index 284c74c..e1717e1 100644 --- a/api/src/firebase/firebase.module.ts +++ b/api/src/firebase/firebase.module.ts @@ -1,5 +1,5 @@ -import { Module } from "@nestjs/common"; -import { FirebaseService } from "./firebase.service.js"; +import { Module } from '@nestjs/common'; +import { FirebaseService } from './firebase.service.js'; @Module({ imports: [], diff --git a/api/src/firebase/firebase.service.ts b/api/src/firebase/firebase.service.ts index fcd6e1c..962a686 100644 --- a/api/src/firebase/firebase.service.ts +++ b/api/src/firebase/firebase.service.ts @@ -1,22 +1,22 @@ -import { Injectable } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; -import admin from "firebase-admin"; -import { FirebaseConfig } from "../config/config.interface.js"; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import admin from 'firebase-admin'; +import { FirebaseConfig } from '../config/config.interface.js'; @Injectable() export class FirebaseService { public readonly app?: admin.app.App; - public constructor( - config: ConfigService, - ) { - const firebaseConfig = config.get("firebase"); + public constructor(config: ConfigService) { + const firebaseConfig = config.get('firebase'); if (firebaseConfig) { - const serviceAccount = JSON.parse(Buffer.from(firebaseConfig.serviceAccount, "base64").toString("utf-8")); + const serviceAccount = JSON.parse( + Buffer.from(firebaseConfig.serviceAccount, 'base64').toString('utf-8'), + ); this.app = admin.initializeApp({ - credential: admin.credential.cert(serviceAccount) + credential: admin.credential.cert(serviceAccount), }); } } -} \ No newline at end of file +} diff --git a/api/src/graphql/bigint.scalar.ts b/api/src/graphql/bigint.scalar.ts index c18cae8..114df8c 100644 --- a/api/src/graphql/bigint.scalar.ts +++ b/api/src/graphql/bigint.scalar.ts @@ -1,13 +1,13 @@ -import { Scalar, CustomScalar } from "@nestjs/graphql"; -import { Kind, ValueNode } from "graphql"; -import BN from "bn.js"; +import { Scalar, CustomScalar } from '@nestjs/graphql'; +import { Kind, ValueNode } from 'graphql'; +import BN from 'bn.js'; -@Scalar("BigInt", (type) => BN) +@Scalar('BigInt', (type) => BN) export class BigIntScalar implements CustomScalar { - public readonly description = "BigInt"; + public readonly description = 'BigInt'; private static INVALID_BIGINT_REPRESENTATION = - "Invalid bigint literal representation. Must be base 10 number"; + 'Invalid bigint literal representation. Must be base 10 number'; private static checkLiteral(value: string) { if (/^[0-9]*$/i.test(value) === false) { diff --git a/api/src/graphql/bytes.scalar.ts b/api/src/graphql/bytes.scalar.ts index dc892ce..a0fc52d 100644 --- a/api/src/graphql/bytes.scalar.ts +++ b/api/src/graphql/bytes.scalar.ts @@ -1,12 +1,11 @@ -import { Scalar, CustomScalar } from "@nestjs/graphql"; -import { Kind, ValueNode } from "graphql"; +import { Scalar, CustomScalar } from '@nestjs/graphql'; +import { Kind, ValueNode } from 'graphql'; -@Scalar("Bytes", (type) => Buffer) +@Scalar('Bytes', (type) => Buffer) export class BytesScalar implements CustomScalar { - public readonly description = "Buffer"; + public readonly description = 'Buffer'; - private static INVALID_HEX_REPRESENTATION = - "Invalid bytes literal hex representation"; + private static INVALID_HEX_REPRESENTATION = 'Invalid bytes literal hex representation'; private static checkLiteral(value: string) { if ((value.length & 1) === 1) { @@ -20,17 +19,17 @@ export class BytesScalar implements CustomScalar { public parseValue(value: string): Buffer { BytesScalar.checkLiteral(value); - return Buffer.from(value, "hex"); + return Buffer.from(value, 'hex'); } public serialize(value: Buffer): string { - return value.toString("hex").toUpperCase(); + return value.toString('hex').toUpperCase(); } public parseLiteral(ast: ValueNode): Buffer { if (ast.kind === Kind.STRING) { BytesScalar.checkLiteral(ast.value); - return Buffer.from(ast.value, "hex"); + return Buffer.from(ast.value, 'hex'); } throw new Error(BytesScalar.INVALID_HEX_REPRESENTATION); } diff --git a/api/src/graphql/decimal.scalar.ts b/api/src/graphql/decimal.scalar.ts index 835a719..4bb33ea 100644 --- a/api/src/graphql/decimal.scalar.ts +++ b/api/src/graphql/decimal.scalar.ts @@ -1,10 +1,10 @@ -import { Scalar, CustomScalar } from "@nestjs/graphql"; -import { Kind, ValueNode } from "graphql"; -import { Decimal } from "decimal.js"; +import { Scalar, CustomScalar } from '@nestjs/graphql'; +import { Kind, ValueNode } from 'graphql'; +import { Decimal } from 'decimal.js'; -@Scalar("Decimal", (type) => Decimal) +@Scalar('Decimal', (type) => Decimal) export class DecimalScalar implements CustomScalar { - public readonly description = "Decimal"; + public readonly description = 'Decimal'; public parseValue(value: string): Decimal { return new Decimal(value); @@ -18,6 +18,6 @@ export class DecimalScalar implements CustomScalar { if (ast.kind === Kind.STRING) { return new Decimal(ast.value); } - throw new Error("Unable to parse Decimal. Literal must be a string."); + throw new Error('Unable to parse Decimal. Literal must be a string.'); } } diff --git a/api/src/graphql/graphql.module.ts b/api/src/graphql/graphql.module.ts index 5e1a61c..e448455 100644 --- a/api/src/graphql/graphql.module.ts +++ b/api/src/graphql/graphql.module.ts @@ -1,8 +1,8 @@ -import { Module } from "@nestjs/common"; -import { LoggingPlugin } from "../graphql/logger.plugin.js"; -import { BytesScalar } from "./bytes.scalar.js"; -import { BigIntScalar } from "./bigint.scalar.js"; -import { DecimalScalar } from "./decimal.scalar.js"; +import { Module } from '@nestjs/common'; +import { LoggingPlugin } from '../graphql/logger.plugin.js'; +import { BytesScalar } from './bytes.scalar.js'; +import { BigIntScalar } from './bigint.scalar.js'; +import { DecimalScalar } from './decimal.scalar.js'; @Module({ imports: [], diff --git a/api/src/graphql/logger.plugin.ts b/api/src/graphql/logger.plugin.ts index 4f35a8c..270e06b 100644 --- a/api/src/graphql/logger.plugin.ts +++ b/api/src/graphql/logger.plugin.ts @@ -1,8 +1,8 @@ -import process from "node:process"; -import { Plugin } from "@nestjs/apollo"; -import { GraphQLRequestContext } from "@apollo/server"; -import { IncomingMessage } from "node:http"; -import { Logger } from "@nestjs/common"; +import process from 'node:process'; +import { Plugin } from '@nestjs/apollo'; +import { GraphQLRequestContext } from '@apollo/server'; +import { IncomingMessage } from 'node:http'; +import { Logger } from '@nestjs/common'; type Req = IncomingMessage & { _startAt?: NodeJS.HRTime; @@ -20,13 +20,13 @@ export class LoggingPlugin { const ctx = context as unknown as GraphQLRequestContext; const { operationName } = ctx.request; - if (operationName === "IntrospectionQuery") { + if (operationName === 'IntrospectionQuery') { return; } const { req } = context.contextValue; - req._startAt = process.hrtime() + req._startAt = process.hrtime(); return { willSendResponse: async () => { @@ -34,7 +34,7 @@ export class LoggingPlugin { const elapsed = process.hrtime(req._startAt); // cover to milliseconds - const ms = (elapsed[0] * 1e3) + (elapsed[1] * 1e-6); + const ms = elapsed[0] * 1e3 + elapsed[1] * 1e-6; const value = ms.toFixed(3); diff --git a/api/src/main.ts b/api/src/main.ts index 07911b8..60e879c 100644 --- a/api/src/main.ts +++ b/api/src/main.ts @@ -1,8 +1,8 @@ -import "dotenv/config"; -import { NestFactory } from "@nestjs/core"; -import { NestExpressApplication } from "@nestjs/platform-express"; -import { AppModule } from "./app/app.module.js"; -import getConfig from "./config/config.js"; +import 'dotenv/config'; +import { NestFactory } from '@nestjs/core'; +import { NestExpressApplication } from '@nestjs/platform-express'; +import { AppModule } from './app/app.module.js'; +import getConfig from './config/config.js'; async function bootstrap() { const config = getConfig(); @@ -11,8 +11,8 @@ async function bootstrap() { app.enableShutdownHooks(); - app.disable("x-powered-by"); - app.set("trust proxy", 1); + app.disable('x-powered-by'); + app.set('trust proxy', 1); app.enableCors(); await app.listen(config.port); diff --git a/api/src/multi-sig/multi-sig.module.ts b/api/src/multi-sig/multi-sig.module.ts index bdf69ee..62fbab4 100644 --- a/api/src/multi-sig/multi-sig.module.ts +++ b/api/src/multi-sig/multi-sig.module.ts @@ -1,6 +1,6 @@ -import { Module } from "@nestjs/common"; -import { ClickhouseModule } from "../clickhouse/clickhouse.module.js"; -import { MultiSigService } from "./multi-sig.service.js"; +import { Module } from '@nestjs/common'; +import { ClickhouseModule } from '../clickhouse/clickhouse.module.js'; +import { MultiSigService } from './multi-sig.service.js'; @Module({ imports: [ClickhouseModule], diff --git a/api/src/multi-sig/multi-sig.service.ts b/api/src/multi-sig/multi-sig.service.ts index ec9f4f0..737f0f8 100644 --- a/api/src/multi-sig/multi-sig.service.ts +++ b/api/src/multi-sig/multi-sig.service.ts @@ -1,5 +1,5 @@ -import { Injectable, OnModuleInit } from "@nestjs/common"; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; @Injectable() export class MultiSigService implements OnModuleInit { @@ -17,4 +17,4 @@ export class MultiSigService implements OnModuleInit { // console.log(rows); // }, 3_000); } -} \ No newline at end of file +} diff --git a/api/src/nats/nats.module.ts b/api/src/nats/nats.module.ts index ce31972..a8e5bc8 100644 --- a/api/src/nats/nats.module.ts +++ b/api/src/nats/nats.module.ts @@ -1,5 +1,5 @@ -import { Module } from "@nestjs/common"; -import { NatsService } from "./nats.service.js"; +import { Module } from '@nestjs/common'; +import { NatsService } from './nats.service.js'; @Module({ providers: [NatsService], diff --git a/api/src/nats/nats.service.ts b/api/src/nats/nats.service.ts index 205a104..8cb27a1 100644 --- a/api/src/nats/nats.service.ts +++ b/api/src/nats/nats.service.ts @@ -1,11 +1,7 @@ -import { - Injectable, - OnApplicationShutdown, - OnModuleInit, -} from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; -import nats, { JetStreamClient, NatsConnection } from "nats"; -import { NatsConfig } from "../config/config.interface.js"; +import { Injectable, OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import nats, { JetStreamClient, NatsConnection } from 'nats'; +import { NatsConfig } from '../config/config.interface.js'; @Injectable() export class NatsService implements OnModuleInit, OnApplicationShutdown { @@ -16,7 +12,7 @@ export class NatsService implements OnModuleInit, OnApplicationShutdown { private servers: string; public constructor(config: ConfigService) { - const natsConfig = config.get("nats")!; + const natsConfig = config.get('nats')!; this.servers = natsConfig?.servers; } @@ -44,7 +40,7 @@ export class NatsService implements OnModuleInit, OnApplicationShutdown { throw new Error(`invalid address length ${address.length}`); } - return `wallet.${Buffer.from(address32).toString("hex").toUpperCase()}.transaction`; + return `wallet.${Buffer.from(address32).toString('hex').toUpperCase()}.transaction`; } public getWalletMovementChannel(address: Uint8Array): string { @@ -57,6 +53,6 @@ export class NatsService implements OnModuleInit, OnApplicationShutdown { throw new Error(`invalid address length ${address.length}`); } - return `wallet.${Buffer.from(address32).toString("hex").toUpperCase()}.movement`; + return `wallet.${Buffer.from(address32).toString('hex').toUpperCase()}.movement`; } } diff --git a/api/src/node-watcher/node-watcher.module.ts b/api/src/node-watcher/node-watcher.module.ts index f04ffd9..ba1ad25 100644 --- a/api/src/node-watcher/node-watcher.module.ts +++ b/api/src/node-watcher/node-watcher.module.ts @@ -1,19 +1,19 @@ -import { Module, Type } from "@nestjs/common"; -import { BullModule } from "@nestjs/bullmq"; +import { Module, Type } from '@nestjs/common'; +import { BullModule } from '@nestjs/bullmq'; -import { NodeWatcherService } from "./node-watcher.service.js"; -import { OlModule } from "../ol/ol.module.js"; -import { PrismaModule } from "../prisma/prisma.module.js"; -import { S3Module } from "../s3/s3.module.js"; -import { NodeResolver } from "./node.resolver.js"; -import RelayController from "./relay.controller.js"; -import { NodeWatcherProcessor } from "./node-watcher.processor.js"; -import { redisClient } from "../redis/redis.service.js"; +import { NodeWatcherService } from './node-watcher.service.js'; +import { OlModule } from '../ol/ol.module.js'; +import { PrismaModule } from '../prisma/prisma.module.js'; +import { S3Module } from '../s3/s3.module.js'; +import { NodeResolver } from './node.resolver.js'; +import RelayController from './relay.controller.js'; +import { NodeWatcherProcessor } from './node-watcher.processor.js'; +import { redisClient } from '../redis/redis.service.js'; -const roles = process.env.ROLES!.split(","); +const roles = process.env.ROLES!.split(','); const workers: Type[] = []; -if (roles.includes("node-watcher")) { +if (roles.includes('node-watcher')) { workers.push(NodeWatcherProcessor); } @@ -24,7 +24,7 @@ if (roles.includes("node-watcher")) { OlModule, BullModule.registerQueue({ - name: "node-watcher", + name: 'node-watcher', connection: redisClient, }), ], diff --git a/api/src/node-watcher/node-watcher.processor.ts b/api/src/node-watcher/node-watcher.processor.ts index 48202a4..05cb9df 100644 --- a/api/src/node-watcher/node-watcher.processor.ts +++ b/api/src/node-watcher/node-watcher.processor.ts @@ -1,14 +1,14 @@ -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import _ from "lodash"; -import { OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import _ from 'lodash'; +import { OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; -import { NodeWatcherService } from "./node-watcher.service.js"; +import { NodeWatcherService } from './node-watcher.service.js'; -@Processor("node-watcher") +@Processor('node-watcher') export class NodeWatcherProcessor extends WorkerHost implements OnModuleInit { public constructor( - @InjectQueue("node-watcher") + @InjectQueue('node-watcher') private readonly nodeWatcherQueue: Queue, private readonly nodeWatcherService: NodeWatcherService, ) { @@ -16,7 +16,7 @@ export class NodeWatcherProcessor extends WorkerHost implements OnModuleInit { } public async onModuleInit() { - await this.nodeWatcherQueue.add("updateValidators", undefined, { + await this.nodeWatcherQueue.add('updateValidators', undefined, { removeOnComplete: { age: 60 * 60, }, @@ -28,7 +28,7 @@ export class NodeWatcherProcessor extends WorkerHost implements OnModuleInit { }, }); - await this.nodeWatcherQueue.add("checkNodes", undefined, { + await this.nodeWatcherQueue.add('checkNodes', undefined, { removeOnComplete: { age: 10, }, @@ -52,10 +52,10 @@ export class NodeWatcherProcessor extends WorkerHost implements OnModuleInit { public async process(job: Job) { switch (job.name) { - case "updateValidators": + case 'updateValidators': await this.updateValidators(); break; - case "checkNodes": + case 'checkNodes': await this.checkNodes(); break; diff --git a/api/src/node-watcher/node-watcher.service.ts b/api/src/node-watcher/node-watcher.service.ts index 362def3..022f3d5 100644 --- a/api/src/node-watcher/node-watcher.service.ts +++ b/api/src/node-watcher/node-watcher.service.ts @@ -1,24 +1,24 @@ -import fs from "node:fs"; -import { Readable } from "node:stream"; +import fs from 'node:fs'; +import { Readable } from 'node:stream'; -import _ from "lodash"; -import { Injectable, Logger } from "@nestjs/common"; -import maxmind, { CityResponse } from "maxmind"; -import { GetObjectCommand } from "@aws-sdk/client-s3"; +import _ from 'lodash'; +import { Injectable, Logger } from '@nestjs/common'; +import maxmind, { CityResponse } from 'maxmind'; +import { GetObjectCommand } from '@aws-sdk/client-s3'; -import { OlService } from "../ol/ol.service.js"; -import { PrismaService } from "../prisma/prisma.service.js"; -import { S3Service } from "../s3/s3.service.js"; -import axios from "axios"; +import { OlService } from '../ol/ol.service.js'; +import { PrismaService } from '../prisma/prisma.service.js'; +import { S3Service } from '../s3/s3.service.js'; +import axios from 'axios'; const LEDGER_VERSION_LIMIT = 30n; const DEFAULT_UPSTREAMS = [ - "172.104.211.8", - "160.202.129.29", - "209.38.172.53", - "38.242.137.192", - "136.243.93.42", - "65.109.80.179", + '172.104.211.8', + '160.202.129.29', + '209.38.172.53', + '38.242.137.192', + '136.243.93.42', + '65.109.80.179', ]; @Injectable() @@ -51,9 +51,7 @@ export class NodeWatcherService { limit = lastVersion.ledgerVersion - LEDGER_VERSION_LIMIT; } - return nodes - .filter((node) => node.ledgerVersion! >= limit) - .map((node) => node.ip); + return nodes.filter((node) => node.ledgerVersion! >= limit).map((node) => node.ip); } // return all the nodes if nothing is up @@ -70,7 +68,7 @@ export class NodeWatcherService { await fs.promises.access(path); return true; } catch (error) { - if (error.code === "ENOENT") { + if (error.code === 'ENOENT') { return false; } throw error; @@ -88,11 +86,11 @@ export class NodeWatcherService { return new Promise((resolve, reject) => { const file = fs.createWriteStream(dest); - file.on("close", () => { + file.on('close', () => { resolve(); }); - file.on("error", (err) => { + file.on('error', (err) => { console.error(err); reject(err); }); @@ -102,14 +100,10 @@ export class NodeWatcherService { } private async downloadGeoIpDb() { - await fs.promises.mkdir(".geoip", { recursive: true }); - - if (!(await this.fileExists(".geoip/GeoLite2-City.mmdb"))) { - await this.downloadFile( - "ol-data", - "geoip/GeoLite2-City.mmdb", - ".geoip/GeoLite2-City.mmdb", - ); + await fs.promises.mkdir('.geoip', { recursive: true }); + + if (!(await this.fileExists('.geoip/GeoLite2-City.mmdb'))) { + await this.downloadFile('ol-data', 'geoip/GeoLite2-City.mmdb', '.geoip/GeoLite2-City.mmdb'); } } @@ -129,9 +123,7 @@ export class NodeWatcherService { GROUP BY "fullNodeIp" `; const ips = res.map(({ ip }) => ip); - const lookup = await maxmind.open( - ".geoip/GeoLite2-City.mmdb", - ); + const lookup = await maxmind.open('.geoip/GeoLite2-City.mmdb'); const nodes: { ip: string; @@ -155,24 +147,17 @@ export class NodeWatcherService { } const placeholders = nodes.map( - (_, i) => - `($${1 + i * 5}, $${2 + i * 5}, $${3 + i * 5}, $${4 + i * 5}, $${5 + i * 5})`, + (_, i) => `($${1 + i * 5}, $${2 + i * 5}, $${3 + i * 5}, $${4 + i * 5}, $${5 + i * 5})`, ); const params = _.flatten( - nodes.map((node) => [ - node.ip, - node.latitude, - node.longitude, - node.city, - node.country, - ]), + nodes.map((node) => [node.ip, node.latitude, node.longitude, node.city, node.country]), ); const nodeIps = nodes.map((node) => node.ip); const query = ` INSERT INTO "Node" ("ip", "latitude", "longitude", "city", "country") - VALUES ${placeholders.join(",")} + VALUES ${placeholders.join(',')} ON CONFLICT ("ip") DO UPDATE SET "latitude" = EXCLUDED."latitude", @@ -187,7 +172,7 @@ export class NodeWatcherService { await this.prisma.$queryRawUnsafe(` DELETE FROM "Node" WHERE "ip" NOT IN ( - ${nodeIps.map((it) => `'${it}'`).join(",")} + ${nodeIps.map((it) => `'${it}'`).join(',')} ) `); } @@ -207,12 +192,12 @@ export class NodeWatcherService { let validatorIp: undefined | string; if (fullnodeAddresses) { - const ip = fullnodeAddresses.split("/"); + const ip = fullnodeAddresses.split('/'); fullNodeIp = ip[2]; } if (networkAddresses) { - const ip = networkAddresses.split("/"); + const ip = networkAddresses.split('/'); validatorIp = ip[2]; } @@ -223,9 +208,7 @@ export class NodeWatcherService { }); } - const placeholders = validators.map( - (_, i) => `($${1 + i * 3}, $${2 + i * 3}, $${3 + i * 3})`, - ); + const placeholders = validators.map((_, i) => `($${1 + i * 3}, $${2 + i * 3}, $${3 + i * 3})`); const params = _.flatten( validators.map((validator) => [ validator.address, @@ -237,7 +220,7 @@ export class NodeWatcherService { const query = ` INSERT INTO "Validator" ("address", "validatorIp", "fullNodeIp") - VALUES ${placeholders.join(",")} + VALUES ${placeholders.join(',')} ON CONFLICT ("address") DO UPDATE SET "validatorIp" = EXCLUDED."validatorIp", @@ -249,7 +232,7 @@ export class NodeWatcherService { await this.prisma.$queryRawUnsafe(` DELETE FROM "Validator" WHERE "address" NOT IN ( - ${validatorAddresses.map((it) => `'\\x${it.toString("hex")}'`).join(",")} + ${validatorAddresses.map((it) => `'\\x${it.toString('hex')}'`).join(',')} ) `); } @@ -258,8 +241,8 @@ export class NodeWatcherService { const nodes = await this.prisma.node.findMany({ orderBy: { lastCheck: { - sort: "asc", - nulls: "first", + sort: 'asc', + nulls: 'first', }, }, take: 10, @@ -271,7 +254,7 @@ export class NodeWatcherService { const now = new Date(); try { const res = await axios({ - method: "GET", + method: 'GET', url: `http://${ip}:8080/v1`, signal: AbortSignal.timeout(5000), //Aborts request after 5 seconds validateStatus: (status) => status === 200, diff --git a/api/src/node-watcher/node.resolver.ts b/api/src/node-watcher/node.resolver.ts index 5fe4819..2b7e5c2 100644 --- a/api/src/node-watcher/node.resolver.ts +++ b/api/src/node-watcher/node.resolver.ts @@ -1,7 +1,7 @@ -import { Query, ObjectType, Field, Float, Resolver } from "@nestjs/graphql"; -import { PrismaService } from "../prisma/prisma.service.js"; +import { Query, ObjectType, Field, Float, Resolver } from '@nestjs/graphql'; +import { PrismaService } from '../prisma/prisma.service.js'; -@ObjectType("Node") +@ObjectType('Node') export class GqlNode { public constructor(latitude: number, longitude: number) { this.latitude = latitude; @@ -22,7 +22,7 @@ export class NodeResolver { @Query(() => [GqlNode], { nullable: true }) public async nodes(): Promise { const nodes = await this.prismaService.node.groupBy({ - by: ["latitude", "longitude"], + by: ['latitude', 'longitude'], }); return nodes.map((node) => new GqlNode(node.latitude, node.longitude)); } diff --git a/api/src/node-watcher/relay.controller.ts b/api/src/node-watcher/relay.controller.ts index 7b3c9b0..1e057b0 100644 --- a/api/src/node-watcher/relay.controller.ts +++ b/api/src/node-watcher/relay.controller.ts @@ -1,18 +1,16 @@ -import { Controller, Get } from "@nestjs/common"; -import { NodeWatcherService } from "./node-watcher.service.js"; +import { Controller, Get } from '@nestjs/common'; +import { NodeWatcherService } from './node-watcher.service.js'; -@Controller("relay") +@Controller('relay') class RelayController { public constructor(private readonly nodeWatcherService: NodeWatcherService) {} - @Get("/upstreams") + @Get('/upstreams') public async getUpstreams() { const ips = await this.nodeWatcherService.getUpstreams(); - return [ - "upstream rpc_ol {", - ips.map((ip) => ` server ${ip}:8080;`).join("\n"), - "}\n", - ].join("\n"); + return ['upstream rpc_ol {', ips.map((ip) => ` server ${ip}:8080;`).join('\n'), '}\n'].join( + '\n', + ); } } diff --git a/api/src/ol-db/ol-db.module.ts b/api/src/ol-db/ol-db.module.ts index 1fce84c..633b04b 100644 --- a/api/src/ol-db/ol-db.module.ts +++ b/api/src/ol-db/ol-db.module.ts @@ -1,6 +1,6 @@ -import { Module } from "@nestjs/common"; -import { ClickhouseModule } from "../clickhouse/clickhouse.module.js"; -import { OlDbService } from "./ol-db.service.js"; +import { Module } from '@nestjs/common'; +import { ClickhouseModule } from '../clickhouse/clickhouse.module.js'; +import { OlDbService } from './ol-db.service.js'; @Module({ imports: [ClickhouseModule], diff --git a/api/src/ol-db/ol-db.service.ts b/api/src/ol-db/ol-db.service.ts index 7a00e05..b4ddf10 100644 --- a/api/src/ol-db/ol-db.service.ts +++ b/api/src/ol-db/ol-db.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import BN from 'bn.js'; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; @Injectable() export class OlDbService { @@ -26,7 +26,7 @@ export class OlDbService { ORDER BY "version" DESC LIMIT 1 `, - format: "JSON", + format: 'JSON', }); const res = await resultSet.json<{ version: string }>(); @@ -42,10 +42,10 @@ export class OlDbService { query: ` SELECT DISTINCT "version" FROM "ingested_versions" - ${after !== undefined ? `WHERE "version" > ${after.toString()}` : ""} + ${after !== undefined ? `WHERE "version" > ${after.toString()}` : ''} ORDER BY "version" `, - format: "JSON", + format: 'JSON', }); const res = await resultSet.json<{ version: string }>(); return res.data.map((it) => new BN(it.version)); diff --git a/api/src/ol-swap/OlSwapModule.ts b/api/src/ol-swap/OlSwapModule.ts index 83b3c6c..777468c 100644 --- a/api/src/ol-swap/OlSwapModule.ts +++ b/api/src/ol-swap/OlSwapModule.ts @@ -1,16 +1,16 @@ -import process from "node:process"; +import process from 'node:process'; -import { BullModule } from "@nestjs/bullmq"; -import { Module, Type } from "@nestjs/common"; +import { BullModule } from '@nestjs/bullmq'; +import { Module, Type } from '@nestjs/common'; -import { redisClient } from "../redis/redis.service.js"; -import { ClickhouseModule } from "../clickhouse/clickhouse.module.js"; -import { OlSwapProcessor } from "./OlSwapProcessor.js"; +import { redisClient } from '../redis/redis.service.js'; +import { ClickhouseModule } from '../clickhouse/clickhouse.module.js'; +import { OlSwapProcessor } from './OlSwapProcessor.js'; -const roles = process.env.ROLES!.split(","); +const roles = process.env.ROLES!.split(','); const workers: Type[] = []; -if (roles.includes("swap-processor")) { +if (roles.includes('swap-processor')) { workers.push(OlSwapProcessor); } @@ -19,7 +19,7 @@ if (roles.includes("swap-processor")) { ClickhouseModule, BullModule.registerQueue({ - name: "ol-swap", + name: 'ol-swap', connection: redisClient, }), ], diff --git a/api/src/ol-swap/OlSwapProcessor.ts b/api/src/ol-swap/OlSwapProcessor.ts index f65f052..63e985d 100644 --- a/api/src/ol-swap/OlSwapProcessor.ts +++ b/api/src/ol-swap/OlSwapProcessor.ts @@ -1,10 +1,10 @@ -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import { OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; -import axios from "axios"; -import { stringify as csvStringify } from 'csv-stringify/sync'; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; -import Bluebird from "bluebird"; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import { OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; +import axios from 'axios'; +import { stringify as csvStringify } from 'csv-stringify/sync'; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; +import Bluebird from 'bluebird'; interface ChartData { date: number; @@ -15,12 +15,12 @@ interface ChartData { volume: number; } -@Processor("ol-swap") +@Processor('ol-swap') export class OlSwapProcessor extends WorkerHost implements OnModuleInit { public constructor( private readonly clickhouseService: ClickhouseService, - @InjectQueue("ol-swap") + @InjectQueue('ol-swap') private readonly olVersionQueue: Queue, ) { super(); @@ -28,7 +28,7 @@ export class OlSwapProcessor extends WorkerHost implements OnModuleInit { public async process(job: Job): Promise { switch (job.name) { - case "getHistory": + case 'getHistory': try { await Promise.race([ this.getHistory(), @@ -46,7 +46,7 @@ export class OlSwapProcessor extends WorkerHost implements OnModuleInit { } public async onModuleInit() { - await this.olVersionQueue.add("getHistory", undefined, { + await this.olVersionQueue.add('getHistory', undefined, { repeat: { every: 30 * 60 * 1_000, // 30 minutes }, @@ -56,7 +56,7 @@ export class OlSwapProcessor extends WorkerHost implements OnModuleInit { private async getHistory() { const res = await axios({ - url: "https://api.0lswap.com/orders/getChartData?interval=1h&market=OLUSDT", + url: 'https://api.0lswap.com/orders/getChartData?interval=1h&market=OLUSDT', signal: AbortSignal.timeout(5 * 60 * 1_000), // 5 minutes }); await this.insertHistory(res.data); @@ -64,14 +64,7 @@ export class OlSwapProcessor extends WorkerHost implements OnModuleInit { private async insertHistory(data: ChartData[]) { const payload = csvStringify( - data.map((it) => [ - it.date, - it.volume, - it.open, - it.high, - it.low, - it.close, - ]), + data.map((it) => [it.date, it.volume, it.open, it.high, it.low, it.close]), ); await this.clickhouseService.client.command({ diff --git a/api/src/ol/account.resolver.ts b/api/src/ol/account.resolver.ts index 3127d49..aa1d91c 100644 --- a/api/src/ol/account.resolver.ts +++ b/api/src/ol/account.resolver.ts @@ -1,19 +1,12 @@ -import { - Args, - Parent, - Query, - ResolveField, - Resolver, - Int, -} from "@nestjs/graphql"; -import { Decimal } from "decimal.js"; +import { Args, Parent, Query, ResolveField, Resolver, Int } from '@nestjs/graphql'; +import { Decimal } from 'decimal.js'; -import { OlService } from "./ol.service.js"; -import { Account } from "./models/account.model.js"; -import { SlowWallet } from "./models/slow-wallet.model.js"; -import { OrderDirection } from "./models/Paginated.js"; -import { PaginatedMovements } from "./models/PaginatedMovements.js"; -import { MovementsService } from "./movements/movements.service.js"; +import { OlService } from './ol.service.js'; +import { Account } from './models/account.model.js'; +import { SlowWallet } from './models/slow-wallet.model.js'; +import { OrderDirection } from './models/Paginated.js'; +import { PaginatedMovements } from './models/PaginatedMovements.js'; +import { MovementsService } from './movements/movements.service.js'; @Resolver(Account) export class AccountResolver { @@ -24,7 +17,7 @@ export class AccountResolver { @Query(() => Account, { nullable: true }) public async account( - @Args({ name: "address", type: () => Buffer }) address: Buffer, + @Args({ name: 'address', type: () => Buffer }) address: Buffer, ): Promise { const accountExists = await this.olService.accountExists(address); if (accountExists) { @@ -39,9 +32,7 @@ export class AccountResolver { } @ResolveField(() => SlowWallet, { nullable: true }) - public async slowWallet( - @Parent() account: Account, - ): Promise { + public async slowWallet(@Parent() account: Account): Promise { return this.olService.getSlowWallet(account.address); } @@ -50,31 +41,26 @@ export class AccountResolver { @Parent() account: Account, @Args({ - name: "first", + name: 'first', type: () => Int, defaultValue: 10, }) first: number, @Args({ - name: "after", + name: 'after', type: () => String, nullable: true, }) after: string | undefined, @Args({ - name: "order", + name: 'order', type: () => OrderDirection, defaultValue: OrderDirection.ASC, }) order: OrderDirection, ): Promise { - return this.movementsService.getPaginatedMovements( - account.address, - first, - after, - order, - ); + return this.movementsService.getPaginatedMovements(account.address, first, after, order); } } diff --git a/api/src/ol/accounts/accounts.model.ts b/api/src/ol/accounts/accounts.model.ts index b14e26f..6dd182f 100644 --- a/api/src/ol/accounts/accounts.model.ts +++ b/api/src/ol/accounts/accounts.model.ts @@ -1,4 +1,4 @@ -import { Field, ObjectType, Int, Float } from "@nestjs/graphql"; +import { Field, ObjectType, Int, Float } from '@nestjs/graphql'; export interface CumulativeShareInput { amount: number; diff --git a/api/src/ol/accounts/accounts.processor.ts b/api/src/ol/accounts/accounts.processor.ts index d468f81..3ccbbee 100644 --- a/api/src/ol/accounts/accounts.processor.ts +++ b/api/src/ol/accounts/accounts.processor.ts @@ -1,15 +1,15 @@ -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import { OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import { OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; -import { AccountsService } from "./accounts.service.js"; -import { redisClient } from "../../redis/redis.service.js"; -import { TOP_BALANCE_ACCOUNTS_CACHE_KEY } from "../constants.js"; +import { AccountsService } from './accounts.service.js'; +import { redisClient } from '../../redis/redis.service.js'; +import { TOP_BALANCE_ACCOUNTS_CACHE_KEY } from '../constants.js'; -@Processor("accounts") +@Processor('accounts') export class AccountsProcessor extends WorkerHost implements OnModuleInit { public constructor( - @InjectQueue("accounts") + @InjectQueue('accounts') private readonly accountsQueue: Queue, private readonly accountsService: AccountsService, @@ -18,7 +18,7 @@ export class AccountsProcessor extends WorkerHost implements OnModuleInit { } public async onModuleInit() { - await this.accountsQueue.add("updateAccountsCache", undefined, { + await this.accountsQueue.add('updateAccountsCache', undefined, { repeat: { every: 60 * 60 * 1_000, // 1 hour }, @@ -27,7 +27,7 @@ export class AccountsProcessor extends WorkerHost implements OnModuleInit { public async process(job: Job) { switch (job.name) { - case "updateAccountsCache": + case 'updateAccountsCache': await this.updateAccountsCache(); break; @@ -38,9 +38,6 @@ export class AccountsProcessor extends WorkerHost implements OnModuleInit { private async updateAccountsCache() { const accounts = await this.accountsService.getTopBalanceAccounts(100); - await redisClient.set( - TOP_BALANCE_ACCOUNTS_CACHE_KEY, - JSON.stringify(accounts), - ); + await redisClient.set(TOP_BALANCE_ACCOUNTS_CACHE_KEY, JSON.stringify(accounts)); } } diff --git a/api/src/ol/accounts/accounts.resolver.ts b/api/src/ol/accounts/accounts.resolver.ts index 91dbbd9..0c38e35 100644 --- a/api/src/ol/accounts/accounts.resolver.ts +++ b/api/src/ol/accounts/accounts.resolver.ts @@ -1,12 +1,12 @@ -import { Query, Resolver, Args } from "@nestjs/graphql"; -import { Inject } from "@nestjs/common"; -import { ServiceUnavailableException } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; +import { Query, Resolver, Args } from '@nestjs/graphql'; +import { Inject } from '@nestjs/common'; +import { ServiceUnavailableException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; -import { CumulativeShare, TopAccount } from "./accounts.model.js"; -import { AccountsService } from "./accounts.service.js"; -import { redisClient } from "../../redis/redis.service.js"; -import { TOP_BALANCE_ACCOUNTS_CACHE_KEY } from "../constants.js"; +import { CumulativeShare, TopAccount } from './accounts.model.js'; +import { AccountsService } from './accounts.service.js'; +import { redisClient } from '../../redis/redis.service.js'; +import { TOP_BALANCE_ACCOUNTS_CACHE_KEY } from '../constants.js'; @Resolver(() => TopAccount) export class AccountsResolver { @@ -17,18 +17,16 @@ export class AccountsResolver { private readonly accountsService: AccountsService, config: ConfigService, ) { - this.cacheEnabled = config.get("cacheEnabled")!; + this.cacheEnabled = config.get('cacheEnabled')!; } @Query(() => [TopAccount]) async getTopAccounts( - @Args("limit", { type: () => Number, defaultValue: 100 }) limit: number, + @Args('limit', { type: () => Number, defaultValue: 100 }) limit: number, ): Promise { // Check if caching is enabled and the query is not present if (this.cacheEnabled) { - const cachedAccounts = await redisClient.get( - TOP_BALANCE_ACCOUNTS_CACHE_KEY, - ); + const cachedAccounts = await redisClient.get(TOP_BALANCE_ACCOUNTS_CACHE_KEY); if (cachedAccounts) { const accounts = JSON.parse(cachedAccounts); return accounts.slice(0, limit).map( @@ -42,7 +40,7 @@ export class AccountsResolver { }), ); } - throw new ServiceUnavailableException("Cache not ready"); + throw new ServiceUnavailableException('Cache not ready'); } // If cache is not enabled, get data from the service diff --git a/api/src/ol/accounts/accounts.service.ts b/api/src/ol/accounts/accounts.service.ts index af67543..9cc6d34 100644 --- a/api/src/ol/accounts/accounts.service.ts +++ b/api/src/ol/accounts/accounts.service.ts @@ -1,8 +1,8 @@ -import { Injectable } from "@nestjs/common"; -import { ClickhouseService } from "../../clickhouse/clickhouse.service.js"; -import { OlService } from "../ol.service.js"; -import { communityWallets } from "../community-wallets/community-wallets.js"; -import { CumulativeShare, TopAccount } from "./accounts.model.js"; +import { Injectable } from '@nestjs/common'; +import { ClickhouseService } from '../../clickhouse/clickhouse.service.js'; +import { OlService } from '../ol.service.js'; +import { communityWallets } from '../community-wallets/community-wallets.js'; +import { CumulativeShare, TopAccount } from './accounts.model.js'; @Injectable() export class AccountsService { @@ -35,7 +35,7 @@ export class AccountsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows: Array<{ @@ -48,7 +48,7 @@ export class AccountsService { let cumulativeBalanceAmount = 0; const accountsWithCumulative = rows.map((account) => { const name = communityWallets.get(account.address)?.name; - account.publicName = name ?? ""; + account.publicName = name ?? ''; cumulativeBalanceAmount += account.balance; const cumulativeShare = new CumulativeShare({ amount: cumulativeBalanceAmount, @@ -62,7 +62,7 @@ export class AccountsService { return accountsWithCumulative; } catch (error) { - console.error("Error in getTopBalanceAccounts:", error); + console.error('Error in getTopBalanceAccounts:', error); throw error; } } diff --git a/api/src/ol/community-wallets/community-wallet.model.ts b/api/src/ol/community-wallets/community-wallet.model.ts index bfbc245..b61f464 100644 --- a/api/src/ol/community-wallets/community-wallet.model.ts +++ b/api/src/ol/community-wallets/community-wallet.model.ts @@ -1,4 +1,4 @@ -import { Field, ObjectType } from "@nestjs/graphql"; +import { Field, ObjectType } from '@nestjs/graphql'; @ObjectType() export class GqlCommunityWallet { diff --git a/api/src/ol/community-wallets/community-wallets.processor.ts b/api/src/ol/community-wallets/community-wallets.processor.ts index daee592..c0d9c42 100644 --- a/api/src/ol/community-wallets/community-wallets.processor.ts +++ b/api/src/ol/community-wallets/community-wallets.processor.ts @@ -1,20 +1,17 @@ -import _ from "lodash"; -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import { Inject, OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; -import { ICommunityWalletsService } from "./interfaces.js"; -import { redisClient } from "../../redis/redis.service.js"; -import { Types } from "../../types.js"; - -import { COMMUNITY_WALLETS_CACHE_KEY } from "../constants.js"; - -@Processor("community-wallets") -export class CommunityWalletsProcessor - extends WorkerHost - implements OnModuleInit -{ +import _ from 'lodash'; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import { Inject, OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; +import { ICommunityWalletsService } from './interfaces.js'; +import { redisClient } from '../../redis/redis.service.js'; +import { Types } from '../../types.js'; + +import { COMMUNITY_WALLETS_CACHE_KEY } from '../constants.js'; + +@Processor('community-wallets') +export class CommunityWalletsProcessor extends WorkerHost implements OnModuleInit { public constructor( - @InjectQueue("community-wallets") + @InjectQueue('community-wallets') private readonly communityWalletsQueue: Queue, @Inject(Types.ICommunityWalletsService) @@ -24,15 +21,11 @@ export class CommunityWalletsProcessor } public async onModuleInit() { - await this.communityWalletsQueue.add( - "updateCommunityWalletsCache", - undefined, - { - repeat: { - every: 60 * 60 * 1_000, // 1 hour - }, + await this.communityWalletsQueue.add('updateCommunityWalletsCache', undefined, { + repeat: { + every: 60 * 60 * 1_000, // 1 hour }, - ); + }); // Execute the job immediately on startup await this.updateCommunityWalletsCache(); @@ -40,7 +33,7 @@ export class CommunityWalletsProcessor public async process(job: Job) { switch (job.name) { - case "updateCommunityWalletsCache": + case 'updateCommunityWalletsCache': await this.updateCommunityWalletsCache(); break; diff --git a/api/src/ol/community-wallets/community-wallets.resolver.ts b/api/src/ol/community-wallets/community-wallets.resolver.ts index 7ff45b0..dcb7a8a 100644 --- a/api/src/ol/community-wallets/community-wallets.resolver.ts +++ b/api/src/ol/community-wallets/community-wallets.resolver.ts @@ -1,12 +1,12 @@ -import { Query, Resolver } from "@nestjs/graphql"; -import _ from "lodash"; -import { Inject } from "@nestjs/common"; -import { redisClient } from "../../redis/redis.service.js"; -import { GqlCommunityWallet } from "./community-wallet.model.js"; -import { ICommunityWalletsService } from "./interfaces.js"; -import { Types } from "../../types.js"; -import { ServiceUnavailableException } from "@nestjs/common"; -import { COMMUNITY_WALLETS_CACHE_KEY } from "../constants.js"; +import { Query, Resolver } from '@nestjs/graphql'; +import _ from 'lodash'; +import { Inject } from '@nestjs/common'; +import { redisClient } from '../../redis/redis.service.js'; +import { GqlCommunityWallet } from './community-wallet.model.js'; +import { ICommunityWalletsService } from './interfaces.js'; +import { Types } from '../../types.js'; +import { ServiceUnavailableException } from '@nestjs/common'; +import { COMMUNITY_WALLETS_CACHE_KEY } from '../constants.js'; @Resolver() export class CommunityWalletsResolver { @@ -16,7 +16,7 @@ export class CommunityWalletsResolver { @Inject(Types.ICommunityWalletsService) private readonly communityWalletsService: ICommunityWalletsService, ) { - this.cacheEnabled = process.env.CACHE_ENABLED === "true"; // Check if cache is enabled + this.cacheEnabled = process.env.CACHE_ENABLED === 'true'; // Check if cache is enabled } @Query(() => [GqlCommunityWallet]) @@ -27,7 +27,7 @@ export class CommunityWalletsResolver { if (cachedWallets) { return JSON.parse(cachedWallets); } - throw new ServiceUnavailableException("Cache not ready"); + throw new ServiceUnavailableException('Cache not ready'); } // If cache is not enabled, fetch data from service diff --git a/api/src/ol/community-wallets/community-wallets.service.ts b/api/src/ol/community-wallets/community-wallets.service.ts index 5291c97..7c4bc26 100644 --- a/api/src/ol/community-wallets/community-wallets.service.ts +++ b/api/src/ol/community-wallets/community-wallets.service.ts @@ -1,37 +1,36 @@ -import { Injectable } from "@nestjs/common"; -import _ from "lodash"; +import { Injectable } from '@nestjs/common'; +import _ from 'lodash'; -import { OlService } from "../ol.service.js"; -import { GqlCommunityWallet } from "./community-wallet.model.js"; -import { parseAddress } from "../../utils.js"; -import { communityWallets } from "./community-wallets.js"; -import { ICommunityWalletsService } from "./interfaces.js"; +import { OlService } from '../ol.service.js'; +import { GqlCommunityWallet } from './community-wallet.model.js'; +import { parseAddress } from '../../utils.js'; +import { communityWallets } from './community-wallets.js'; +import { ICommunityWalletsService } from './interfaces.js'; @Injectable() export class CommunityWalletsService implements ICommunityWalletsService { public constructor(private readonly olService: OlService) {} public async getCommunityWallets(): Promise { - const donorVoiceRegistry = - (await this.olService.aptosClient.getAccountResource( - "0x1", - "0x1::donor_voice::Registry", - )) as { - type: "0x1::donor_voice::Registry"; - data: { - liquidation_queue: []; - list: string[]; - }; + const donorVoiceRegistry = (await this.olService.aptosClient.getAccountResource( + '0x1', + '0x1::donor_voice::Registry', + )) as { + type: '0x1::donor_voice::Registry'; + data: { + liquidation_queue: []; + list: string[]; }; + }; const addresses = donorVoiceRegistry.data.list.map((address) => - parseAddress(address).toString("hex").toUpperCase(), + parseAddress(address).toString('hex').toUpperCase(), ); const res = await Promise.all( addresses.map(async (address) => { const addrBuff = parseAddress(address); - const addr = addrBuff.toString("hex").toUpperCase(); + const addr = addrBuff.toString('hex').toUpperCase(); const info = communityWallets.get(addr); const balance = await this.olService.getAccountBalance(addrBuff); diff --git a/api/src/ol/community-wallets/community-wallets.ts b/api/src/ol/community-wallets/community-wallets.ts index 8d9ccc0..a225d7e 100644 --- a/api/src/ol/community-wallets/community-wallets.ts +++ b/api/src/ol/community-wallets/community-wallets.ts @@ -5,110 +5,107 @@ interface CommunityWalletInfo { export const communityWallets = new Map([ [ - "BC25F79FEF8A981BE4636AC1A2D6F587", + 'BC25F79FEF8A981BE4636AC1A2D6F587', { - name: "Application Studio", + name: 'Application Studio', description: - "Newlab started this program to back teams that have clearly-defined plans to leverage 0L for applications with real-world, measurable impact and utility.", + 'Newlab started this program to back teams that have clearly-defined plans to leverage 0L for applications with real-world, measurable impact and utility.', }, ], [ - "2B0E8325DEA5BE93D856CFDE2D0CBA12", + '2B0E8325DEA5BE93D856CFDE2D0CBA12', { - name: "Tip Jar", + name: 'Tip Jar', description: - "A personal tip jar for the lead 0L developer, who has contributed thousands of hours of work to the project.", + 'A personal tip jar for the lead 0L developer, who has contributed thousands of hours of work to the project.', }, ], [ - "BCA50D10041FA111D1B44181A264A599", + 'BCA50D10041FA111D1B44181A264A599', { - name: "A Good List", + name: 'A Good List', description: - "Supports a collection of non-profit and humanitarian organizations. Contributors can vote on the weighting.", + 'Supports a collection of non-profit and humanitarian organizations. Contributors can vote on the weighting.', }, ], [ - "B31BD7796BC113013A2BF6C3953305FD", + 'B31BD7796BC113013A2BF6C3953305FD', { - name: "Danish Red Cross Humanitarian Fund", + name: 'Danish Red Cross Humanitarian Fund', description: - "Under the auspices of Red Cross humanitarian principles, the Fund aims to improve outcomes for communities affected by humanitarian crises.", + 'Under the auspices of Red Cross humanitarian principles, the Fund aims to improve outcomes for communities affected by humanitarian crises.', }, ], [ - "3A6C51A0B786D644590E8A21591FA8E2", + '3A6C51A0B786D644590E8A21591FA8E2', { - name: "Ongoing Full-Time Workers Program", + name: 'Ongoing Full-Time Workers Program', description: - "The iqlusion FTW Program aims to collect ongoing donations, and redistribute those donations to any engineers working full-time on the 0L platform on a monthly basis (collectively the FTW).", + 'The iqlusion FTW Program aims to collect ongoing donations, and redistribute those donations to any engineers working full-time on the 0L platform on a monthly basis (collectively the FTW).', }, ], [ - "19E966BFA4B32CE9B7E23721B37B96D2", + '19E966BFA4B32CE9B7E23721B37B96D2', { - name: "Social Infrastructure Program", + name: 'Social Infrastructure Program', description: - "This program is designed to provide capital to fund a wide range of benefits to 0L members in alignment with the mission, vision and values of the community.", + 'This program is designed to provide capital to fund a wide range of benefits to 0L members in alignment with the mission, vision and values of the community.', }, ], [ - "2057BCFB0189B7FD0ABA7244BA271661", + '2057BCFB0189B7FD0ABA7244BA271661', { - name: "Moonshot Program", + name: 'Moonshot Program', description: - "Inspired by the well-known Xprize awards, we think large and meaningful rewards are necessary to materialize frontier technologies which are ambitious, speculative, and non-obvious.", + 'Inspired by the well-known Xprize awards, we think large and meaningful rewards are necessary to materialize frontier technologies which are ambitious, speculative, and non-obvious.', }, ], [ - "F605FE7F787551EEA808EE9ACDB98897", + 'F605FE7F787551EEA808EE9ACDB98897', { - name: "Human Rewards Program", + name: 'Human Rewards Program', description: - "A program to allow anyone that can verify they are human to receive some coins for some human work.", + 'A program to allow anyone that can verify they are human to receive some coins for some human work.', }, ], [ - "BB6926434D1497A559E4F0487F79434F", + 'BB6926434D1497A559E4F0487F79434F', { - name: "Deep Technology Innovation Program", + name: 'Deep Technology Innovation Program', description: - "BlockScience has established this Program to create a pathway for funding to the academic fields that technology and other crypto networks rely upon.", + 'BlockScience has established this Program to create a pathway for funding to the academic fields that technology and other crypto networks rely upon.', }, ], [ - "1367B68C86CB27FA7215D9F75A26EB8F", + '1367B68C86CB27FA7215D9F75A26EB8F', { - name: "University of Toronto MSRG", + name: 'University of Toronto MSRG', description: - "This program seeks to attract funding to help support basic research & discovery targeted at distributed ledger and blockchain technology as well as distributed systems in the long term.", + 'This program seeks to attract funding to help support basic research & discovery targeted at distributed ledger and blockchain technology as well as distributed systems in the long term.', }, ], [ - "C906F67F626683B77145D1F20C1A753B", + 'C906F67F626683B77145D1F20C1A753B', { - name: "The Iqlusion Engineering Fund", + name: 'The Iqlusion Engineering Fund', description: - "This program will establish and compensate community members engaged in engineering work whether original work, maintenance, or review of the code.", + 'This program will establish and compensate community members engaged in engineering work whether original work, maintenance, or review of the code.', }, ], [ - "C19C06A592911ED31C4100E9FB63AD7B", + 'C19C06A592911ED31C4100E9FB63AD7B', { - name: "RxC Research and Experimentation", + name: 'RxC Research and Experimentation', description: - "The RadicalxChange Foundation has established this Research and Experimentation fund to advance new incentive structures.", + 'The RadicalxChange Foundation has established this Research and Experimentation fund to advance new incentive structures.', }, ], [ - "87DC2E497AC6EDAB21511333A421E5A5", + '87DC2E497AC6EDAB21511333A421E5A5', { - name: "Working Group Key Roles", + name: 'Working Group Key Roles', }, ], - [ - "2640CD6D652AC94DC5F0963DCC00BCC7", - { name: "Engineering Fund, tool-scrubbers-guild" }, - ], - ["FBE8DA53C92CEEEB40D8967EC033A0FB", { name: "Community development" }], + ['2640CD6D652AC94DC5F0963DCC00BCC7', { name: 'Engineering Fund, tool-scrubbers-guild' }], + ['FBE8DA53C92CEEEB40D8967EC033A0FB', { name: 'Community development' }], ]); diff --git a/api/src/ol/community-wallets/interfaces.ts b/api/src/ol/community-wallets/interfaces.ts index e765096..846b1b6 100644 --- a/api/src/ol/community-wallets/interfaces.ts +++ b/api/src/ol/community-wallets/interfaces.ts @@ -1,5 +1,5 @@ -import { GqlCommunityWallet } from "./community-wallet.model.js"; +import { GqlCommunityWallet } from './community-wallet.model.js'; export interface ICommunityWalletsService { getCommunityWallets(): Promise; -} \ No newline at end of file +} diff --git a/api/src/ol/constants.ts b/api/src/ol/constants.ts index d4eb4a6..84abbb7 100644 --- a/api/src/ol/constants.ts +++ b/api/src/ol/constants.ts @@ -1,4 +1,4 @@ export const V0_TIMESTAMP = 1712696400; -export const TOP_BALANCE_ACCOUNTS_CACHE_KEY = "__0L_TOP_BALANCE_ACCOUNTS__"; -export const COMMUNITY_WALLETS_CACHE_KEY = "__0L_COMMUNITY_WALLETS__"; -export const VALIDATORS_CACHE_KEY = "__0L_VALIDATORS__"; +export const TOP_BALANCE_ACCOUNTS_CACHE_KEY = '__0L_TOP_BALANCE_ACCOUNTS__'; +export const COMMUNITY_WALLETS_CACHE_KEY = '__0L_COMMUNITY_WALLETS__'; +export const VALIDATORS_CACHE_KEY = '__0L_VALIDATORS__'; diff --git a/api/src/ol/info.resolver.ts b/api/src/ol/info.resolver.ts index 2b5088a..480edad 100644 --- a/api/src/ol/info.resolver.ts +++ b/api/src/ol/info.resolver.ts @@ -1,11 +1,11 @@ -import { Query, Resolver } from "@nestjs/graphql"; -import { Inject } from "@nestjs/common"; +import { Query, Resolver } from '@nestjs/graphql'; +import { Inject } from '@nestjs/common'; -import { Info } from "./models/info.model.js"; -import { OlService } from "./ol.service.js"; -import { Types } from "../types.js"; -import { IOnChainTransactionsRepository } from "./transactions/interfaces.js"; -import BN from "bn.js"; +import { Info } from './models/info.model.js'; +import { OlService } from './ol.service.js'; +import { Types } from '../types.js'; +import { IOnChainTransactionsRepository } from './transactions/interfaces.js'; +import BN from 'bn.js'; @Resolver() export class InfoResolver { @@ -14,19 +14,17 @@ export class InfoResolver { @Inject(Types.IOnChainTransactionsRepository) private readonly onChainTransactionsRepository: IOnChainTransactionsRepository, - ) { - } - + ) {} @Query(() => Info) public async info(): Promise { const latestStableVersion = await this.olService.getLatestStableVersion(); let latestStableTimestamp: BN | null = null; if (latestStableVersion) { - latestStableTimestamp = await this.onChainTransactionsRepository.getTransactionTimestamp(latestStableVersion); + latestStableTimestamp = + await this.onChainTransactionsRepository.getTransactionTimestamp(latestStableVersion); } return new Info({ latestStableVersion, latestStableTimestamp }); } } - diff --git a/api/src/ol/models/AbstractTransaction.ts b/api/src/ol/models/AbstractTransaction.ts index d8190cb..13767f1 100644 --- a/api/src/ol/models/AbstractTransaction.ts +++ b/api/src/ol/models/AbstractTransaction.ts @@ -1,12 +1,12 @@ -import { Field, InterfaceType } from "@nestjs/graphql"; -import BN from "bn.js"; +import { Field, InterfaceType } from '@nestjs/graphql'; +import BN from 'bn.js'; export interface AbstractTransactionInput { version: BN; hash: Uint8Array; } -@InterfaceType("AbstractTransaction") +@InterfaceType('AbstractTransaction') export abstract class AbstractTransaction { @Field(() => BN) version: BN; diff --git a/api/src/ol/models/BlockMetadataTransaction.ts b/api/src/ol/models/BlockMetadataTransaction.ts index 6d11785..c05dd08 100644 --- a/api/src/ol/models/BlockMetadataTransaction.ts +++ b/api/src/ol/models/BlockMetadataTransaction.ts @@ -1,17 +1,14 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import BN from "bn.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import BN from 'bn.js'; -import { - AbstractTransactionInput, - AbstractTransaction, -} from "./AbstractTransaction.js"; +import { AbstractTransactionInput, AbstractTransaction } from './AbstractTransaction.js'; export type BlockMetadataTransactionInput = AbstractTransactionInput & { epoch: BN; timestamp: BN; }; -@ObjectType("BlockMetadataTransaction", { +@ObjectType('BlockMetadataTransaction', { implements: () => [AbstractTransaction], }) export class BlockMetadataTransaction implements AbstractTransaction { diff --git a/api/src/ol/models/GenesisTransaction.ts b/api/src/ol/models/GenesisTransaction.ts index afcca2e..c6770a8 100644 --- a/api/src/ol/models/GenesisTransaction.ts +++ b/api/src/ol/models/GenesisTransaction.ts @@ -1,14 +1,11 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import BN from "bn.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import BN from 'bn.js'; -import { - AbstractTransactionInput, - AbstractTransaction, -} from "./AbstractTransaction.js"; +import { AbstractTransactionInput, AbstractTransaction } from './AbstractTransaction.js'; export type GenesisTransactionInput = AbstractTransactionInput; -@ObjectType("GenesisTransaction", { +@ObjectType('GenesisTransaction', { implements: () => [AbstractTransaction], }) export class GenesisTransaction implements AbstractTransaction { diff --git a/api/src/ol/models/Movement.ts b/api/src/ol/models/Movement.ts index 41e3612..c161b32 100644 --- a/api/src/ol/models/Movement.ts +++ b/api/src/ol/models/Movement.ts @@ -1,8 +1,8 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import BN from "bn.js"; -import { Decimal } from "decimal.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import BN from 'bn.js'; +import { Decimal } from 'decimal.js'; -import { AbstractTransaction } from "./AbstractTransaction.js"; +import { AbstractTransaction } from './AbstractTransaction.js'; export interface MovementInput { amount: Decimal; @@ -15,7 +15,7 @@ export interface MovementInput { transaction: AbstractTransaction; } -@ObjectType("Movement") +@ObjectType('Movement') export class Movement { public constructor(input: MovementInput) { this.balance = input.balance; diff --git a/api/src/ol/models/Paginated.ts b/api/src/ol/models/Paginated.ts index a71a202..0ff1a75 100644 --- a/api/src/ol/models/Paginated.ts +++ b/api/src/ol/models/Paginated.ts @@ -1,12 +1,12 @@ -import { ObjectType, Field, registerEnumType } from "@nestjs/graphql"; -import { Type } from "@nestjs/common"; +import { ObjectType, Field, registerEnumType } from '@nestjs/graphql'; +import { Type } from '@nestjs/common'; export enum OrderDirection { - ASC = "ASC", - DESC = "DESC", + ASC = 'ASC', + DESC = 'DESC', } -registerEnumType(OrderDirection, { name: "OrderDirection" }); +registerEnumType(OrderDirection, { name: 'OrderDirection' }); export interface IEdgeType { cursor: string; @@ -19,7 +19,7 @@ export interface IPaginatedType { pageInfo: PageInfo; } -@ObjectType("PageInfo") +@ObjectType('PageInfo') export class PageInfo { @Field((type) => String, { nullable: true }) public readonly prevCursor?: string; @@ -67,9 +67,7 @@ export function Paginated(classRef: Type): Type> { ) { this.totalCount = totalCount; this.pageInfo = pageInfo; - this.edges = nodes.map( - (node) => new EdgeType(cursorExtractor(node), node), - ); + this.edges = nodes.map((node) => new EdgeType(cursorExtractor(node), node)); } } return PaginatedType as Type>; diff --git a/api/src/ol/models/PaginatedMovements.ts b/api/src/ol/models/PaginatedMovements.ts index 2ffdd23..0a9367f 100644 --- a/api/src/ol/models/PaginatedMovements.ts +++ b/api/src/ol/models/PaginatedMovements.ts @@ -1,17 +1,11 @@ -import { ObjectType } from "@nestjs/graphql"; +import { ObjectType } from '@nestjs/graphql'; -import { Movement } from "./Movement.js"; -import { Paginated, PageInfo } from "./Paginated.js"; +import { Movement } from './Movement.js'; +import { Paginated, PageInfo } from './Paginated.js'; @ObjectType() export class PaginatedMovements extends Paginated(Movement) { - public constructor( - totalCount: number, - pageInfo: PageInfo, - nodes: Movement[], - ) { - super(totalCount, pageInfo, nodes, (movement: Movement) => - movement.version.toString(10), - ); + public constructor(totalCount: number, pageInfo: PageInfo, nodes: Movement[]) { + super(totalCount, pageInfo, nodes, (movement: Movement) => movement.version.toString(10)); } } diff --git a/api/src/ol/models/ScriptUserTransaction.ts b/api/src/ol/models/ScriptUserTransaction.ts index ef93dae..b29c25e 100644 --- a/api/src/ol/models/ScriptUserTransaction.ts +++ b/api/src/ol/models/ScriptUserTransaction.ts @@ -1,10 +1,7 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import BN from "bn.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import BN from 'bn.js'; -import { - AbstractTransactionInput, - AbstractTransaction, -} from "./AbstractTransaction.js"; +import { AbstractTransactionInput, AbstractTransaction } from './AbstractTransaction.js'; export type ScriptUserTransactionInput = AbstractTransactionInput & { sender: Buffer; @@ -12,7 +9,7 @@ export type ScriptUserTransactionInput = AbstractTransactionInput & { timestamp: BN; }; -@ObjectType("ScriptUserTransaction", { +@ObjectType('ScriptUserTransaction', { implements: () => [AbstractTransaction], }) export class ScriptUserTransaction implements AbstractTransaction { diff --git a/api/src/ol/models/Transaction.ts b/api/src/ol/models/Transaction.ts index c2adf50..f49b908 100644 --- a/api/src/ol/models/Transaction.ts +++ b/api/src/ol/models/Transaction.ts @@ -1,9 +1,9 @@ -import { createUnionType } from "@nestjs/graphql"; +import { createUnionType } from '@nestjs/graphql'; -import { BlockMetadataTransaction } from "./BlockMetadataTransaction.js"; -import { GenesisTransaction } from "./GenesisTransaction.js"; -import { UserTransaction } from "./UserTransaction.js"; -import { ScriptUserTransaction } from "./ScriptUserTransaction.js"; +import { BlockMetadataTransaction } from './BlockMetadataTransaction.js'; +import { GenesisTransaction } from './GenesisTransaction.js'; +import { UserTransaction } from './UserTransaction.js'; +import { ScriptUserTransaction } from './ScriptUserTransaction.js'; export type AbstractTransaction = | GenesisTransaction @@ -12,12 +12,7 @@ export type AbstractTransaction = | ScriptUserTransaction; export const GqlTransaction = createUnionType({ - name: "ChainTransaction", + name: 'ChainTransaction', types: () => - [ - GenesisTransaction, - BlockMetadataTransaction, - UserTransaction, - ScriptUserTransaction, - ] as const, + [GenesisTransaction, BlockMetadataTransaction, UserTransaction, ScriptUserTransaction] as const, }); diff --git a/api/src/ol/models/UserTransaction.ts b/api/src/ol/models/UserTransaction.ts index fa42f0c..5ae06fc 100644 --- a/api/src/ol/models/UserTransaction.ts +++ b/api/src/ol/models/UserTransaction.ts @@ -1,10 +1,7 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import BN from "bn.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import BN from 'bn.js'; -import { - AbstractTransactionInput, - AbstractTransaction, -} from "./AbstractTransaction.js"; +import { AbstractTransactionInput, AbstractTransaction } from './AbstractTransaction.js'; export type UserTransactionInput = AbstractTransactionInput & { sender: Buffer; @@ -16,7 +13,7 @@ export type UserTransactionInput = AbstractTransactionInput & { timestamp: BN; }; -@ObjectType("UserTransaction", { +@ObjectType('UserTransaction', { implements: () => [AbstractTransaction], }) export class UserTransaction implements AbstractTransaction { diff --git a/api/src/ol/models/account.model.ts b/api/src/ol/models/account.model.ts index b185102..a55d39e 100644 --- a/api/src/ol/models/account.model.ts +++ b/api/src/ol/models/account.model.ts @@ -8,4 +8,4 @@ export class Account { @Field(() => Buffer) public address: Buffer; -} \ No newline at end of file +} diff --git a/api/src/ol/models/info.model.ts b/api/src/ol/models/info.model.ts index 60b2005..c674c9c 100644 --- a/api/src/ol/models/info.model.ts +++ b/api/src/ol/models/info.model.ts @@ -1,12 +1,12 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import BN from "bn.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import BN from 'bn.js'; interface InfoArgs { latestStableVersion: BN | null; latestStableTimestamp: BN | null; } -@ObjectType("Info") +@ObjectType('Info') export class Info { @Field(() => BN, { nullable: true }) public latestStableVersion: BN | null; @@ -18,4 +18,4 @@ export class Info { this.latestStableVersion = args.latestStableVersion; this.latestStableTimestamp = args.latestStableTimestamp; } -} \ No newline at end of file +} diff --git a/api/src/ol/models/modules.model.ts b/api/src/ol/models/modules.model.ts index b5e3d67..d2b37ff 100644 --- a/api/src/ol/models/modules.model.ts +++ b/api/src/ol/models/modules.model.ts @@ -1,4 +1,4 @@ -import { Field, ObjectType } from "@nestjs/graphql"; +import { Field, ObjectType } from '@nestjs/graphql'; interface GqlModuleInput { address: string; @@ -6,7 +6,7 @@ interface GqlModuleInput { functions: string[]; } -@ObjectType("Module") +@ObjectType('Module') export class GqlModule { public constructor(input: GqlModuleInput) { this.address = input.address; diff --git a/api/src/ol/models/slow-wallet.model.ts b/api/src/ol/models/slow-wallet.model.ts index 3478c41..211a947 100644 --- a/api/src/ol/models/slow-wallet.model.ts +++ b/api/src/ol/models/slow-wallet.model.ts @@ -1,5 +1,5 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import { Decimal } from "decimal.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import { Decimal } from 'decimal.js'; interface SlowWalletInput { transferred: Decimal; diff --git a/api/src/ol/models/system-info.model.ts b/api/src/ol/models/system-info.model.ts index e01abef..9d11b5c 100644 --- a/api/src/ol/models/system-info.model.ts +++ b/api/src/ol/models/system-info.model.ts @@ -12,4 +12,4 @@ export class GqlSystemInfo { @Field(() => Number) public consensusReward: number; -} \ No newline at end of file +} diff --git a/api/src/ol/models/transaction.model.ts b/api/src/ol/models/transaction.model.ts index fc05a6f..991a82f 100644 --- a/api/src/ol/models/transaction.model.ts +++ b/api/src/ol/models/transaction.model.ts @@ -1,4 +1,4 @@ -import { Field, ObjectType } from "@nestjs/graphql"; +import { Field, ObjectType } from '@nestjs/graphql'; interface GqlUserTransactionInput { hash: string; @@ -18,7 +18,7 @@ interface GqlUserTransactionInput { timestamp: number; } -@ObjectType("GqlUserTransactionDeprecated") +@ObjectType('GqlUserTransactionDeprecated') export class GqlUserTransactionDeprecated { public constructor(input: GqlUserTransactionInput) { this.hash = input.hash; @@ -84,7 +84,7 @@ export class GqlUserTransactionDeprecated { public timestamp: number; } -@ObjectType("UserTransactionCollection") +@ObjectType('UserTransactionCollection') export class GqlUserTransactionCollection { public constructor(size: number, items: GqlUserTransactionDeprecated[]) { this.size = size; diff --git a/api/src/ol/models/validator.model.ts b/api/src/ol/models/validator.model.ts index e9ccaea..3700265 100644 --- a/api/src/ol/models/validator.model.ts +++ b/api/src/ol/models/validator.model.ts @@ -1,7 +1,7 @@ -import { Field, ObjectType } from "@nestjs/graphql"; -import BN from "bn.js"; +import { Field, ObjectType } from '@nestjs/graphql'; +import BN from 'bn.js'; -@ObjectType("ValidatorCurrentBid") +@ObjectType('ValidatorCurrentBid') export class GqlValidatorCurrentBid { public constructor(input: GqlValidatorCurrentBidInput) { this.currentBid = input.currentBid; @@ -26,7 +26,7 @@ interface GqlValidatorGradeInput { failedBlocks: number; } -@ObjectType("ValidatorGrade") +@ObjectType('ValidatorGrade') export class GqlValidatorGrade { public constructor(input: GqlValidatorGradeInput) { this.compliant = input.compliant; @@ -118,7 +118,7 @@ interface GqlVouchInput { address: string; } -@ObjectType("Vouch") +@ObjectType('Vouch') export class GqlVouch { public constructor(input: GqlVouchInput) { this.epoch = input.epoch; diff --git a/api/src/ol/modules.resolver.ts b/api/src/ol/modules.resolver.ts index 9bed0d1..060a8f8 100644 --- a/api/src/ol/modules.resolver.ts +++ b/api/src/ol/modules.resolver.ts @@ -1,15 +1,14 @@ -import { Query, Resolver } from "@nestjs/graphql"; +import { Query, Resolver } from '@nestjs/graphql'; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; -import { GqlModule } from "./models/modules.model.js"; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; +import { GqlModule } from './models/modules.model.js'; @Resolver() export class ModulesResolver { public constructor(private readonly clickhouseService: ClickhouseService) {} @Query(() => [GqlModule]) - async modules( - ): Promise { + async modules(): Promise { const accountsModules = new Map>(); const rows = await this.clickhouseService.client @@ -26,7 +25,7 @@ export class ModulesResolver { "module_name", "function_name" `, - format: "JSONEachRow", + format: 'JSONEachRow', }) .then((res) => res.json<{ @@ -42,9 +41,9 @@ export class ModulesResolver { functionName: row.function_name, })), ); - + for (const row of rows) { - let accountModules = accountsModules.get(row.moduleAddress) + let accountModules = accountsModules.get(row.moduleAddress); if (!accountModules) { accountModules = new Map(); accountsModules.set(row.moduleAddress, accountModules); diff --git a/api/src/ol/movements/movements.resolver.ts b/api/src/ol/movements/movements.resolver.ts index 1eff14e..220d1a0 100644 --- a/api/src/ol/movements/movements.resolver.ts +++ b/api/src/ol/movements/movements.resolver.ts @@ -1,7 +1,7 @@ -import { Args, Resolver, Subscription } from "@nestjs/graphql"; -import { Repeater } from "@repeaterjs/repeater"; +import { Args, Resolver, Subscription } from '@nestjs/graphql'; +import { Repeater } from '@repeaterjs/repeater'; -import { NatsService } from "../../nats/nats.service.js"; +import { NatsService } from '../../nats/nats.service.js'; @Resolver() export class MovementsResolver { @@ -9,7 +9,7 @@ export class MovementsResolver { @Subscription((returns) => String) public async walletMovement( - @Args({ name: "address", type: () => Buffer }) + @Args({ name: 'address', type: () => Buffer }) address: Buffer, ) { return new Repeater(async (push, stop) => { diff --git a/api/src/ol/movements/movements.service.ts b/api/src/ol/movements/movements.service.ts index ea98604..6de4426 100644 --- a/api/src/ol/movements/movements.service.ts +++ b/api/src/ol/movements/movements.service.ts @@ -1,18 +1,18 @@ -import { Inject, Injectable } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; -import * as d3 from "d3-array"; -import axios from "axios"; -import BN from "bn.js"; -import { Decimal } from "decimal.js"; - -import { NatsService } from "../../nats/nats.service.js"; -import { OlConfig } from "../../config/config.interface.js"; -import { Movement } from "../models/Movement.js"; -import { OrderDirection, PageInfo } from "../models/Paginated.js"; -import { PaginatedMovements } from "../models/PaginatedMovements.js"; -import { IOnChainTransactionsRepository } from "../transactions/interfaces.js"; -import { Types } from "../../types.js"; -import { OlService } from "../ol.service.js"; +import { Inject, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import * as d3 from 'd3-array'; +import axios from 'axios'; +import BN from 'bn.js'; +import { Decimal } from 'decimal.js'; + +import { NatsService } from '../../nats/nats.service.js'; +import { OlConfig } from '../../config/config.interface.js'; +import { Movement } from '../models/Movement.js'; +import { OrderDirection, PageInfo } from '../models/Paginated.js'; +import { PaginatedMovements } from '../models/PaginatedMovements.js'; +import { IOnChainTransactionsRepository } from '../transactions/interfaces.js'; +import { Types } from '../../types.js'; +import { OlService } from '../ol.service.js'; @Injectable() export class MovementsService { @@ -26,7 +26,7 @@ export class MovementsService { @Inject(Types.IOnChainTransactionsRepository) private readonly onChainTransactionsRepository: IOnChainTransactionsRepository, ) { - const olConfig = configService.get("ol")!; + const olConfig = configService.get('ol')!; this.dataApiHost = olConfig.dataApiHost; } @@ -43,7 +43,7 @@ export class MovementsService { return new PaginatedMovements(0, new PageInfo(false), []); } const lastestStableVersion = lastestStableVersionBn.toNumber(); - const accountAddress = bAccountAddress.toString("hex").toUpperCase(); + const accountAddress = bAccountAddress.toString('hex').toUpperCase(); // Retrieve all the wallets movements from the data api const historicalData = await axios<{ @@ -53,7 +53,7 @@ export class MovementsService { unlocked: number[]; locked: number[]; }>({ - method: "GET", + method: 'GET', url: `${this.dataApiHost}/historical-balance/${accountAddress}`, }); @@ -62,8 +62,7 @@ export class MovementsService { * @todo * move this to the data api */ - const { timestamp, version, balance, unlocked, locked } = - historicalData.data; + const { timestamp, version, balance, unlocked, locked } = historicalData.data; let len = timestamp.length; if (len > 1) { @@ -87,9 +86,7 @@ export class MovementsService { } } - const allVersions = historicalData.data.version.filter( - (v) => v <= lastestStableVersion, - ); + const allVersions = historicalData.data.version.filter((v) => v <= lastestStableVersion); const allVersionsLength = allVersions.length; if (!allVersionsLength) { return new PaginatedMovements(0, new PageInfo(false), []); @@ -102,10 +99,7 @@ export class MovementsService { switch (order) { case OrderDirection.ASC: { - startIndex = - after === undefined - ? 0 - : d3.bisectRight(allVersions, parseInt(after, 10)); + startIndex = after === undefined ? 0 : d3.bisectRight(allVersions, parseInt(after, 10)); endIndex = Math.min(allVersionsLength, startIndex + first); prevIndex = startIndex - first - 1; @@ -139,18 +133,10 @@ export class MovementsService { scriptUserTransactions, genesisTransactions, ] = await Promise.all([ - this.onChainTransactionsRepository.getUserTransactionsByVersions( - versions, - ), - this.onChainTransactionsRepository.getBlockMetadataTransactionsByVersions( - versions, - ), - this.onChainTransactionsRepository.getScriptUserTransactionsByVersions( - versions, - ), - this.onChainTransactionsRepository.getGenesisTransactionsByVersions( - versions, - ), + this.onChainTransactionsRepository.getUserTransactionsByVersions(versions), + this.onChainTransactionsRepository.getBlockMetadataTransactionsByVersions(versions), + this.onChainTransactionsRepository.getScriptUserTransactionsByVersions(versions), + this.onChainTransactionsRepository.getGenesisTransactionsByVersions(versions), ]); const movements = versions.map((version, index) => { @@ -163,15 +149,9 @@ export class MovementsService { const pos = startIndex + index; - const unlockedBalance = new Decimal( - historicalData.data.unlocked[index + startIndex], - ); - const lockedBalance = new Decimal( - historicalData.data.locked[index + startIndex], - ); - const balance = new Decimal( - historicalData.data.balance[index + startIndex], - ); + const unlockedBalance = new Decimal(historicalData.data.unlocked[index + startIndex]); + const lockedBalance = new Decimal(historicalData.data.locked[index + startIndex]); + const balance = new Decimal(historicalData.data.balance[index + startIndex]); let unlockedAmount = unlockedBalance; let lockedAmount = lockedBalance; @@ -179,12 +159,8 @@ export class MovementsService { if (pos > 0) { const prevBalance = new Decimal(historicalData.data.balance[pos - 1]); - const prevLockedBalance = new Decimal( - historicalData.data.locked[pos - 1], - ); - const prevUnlockedBalance = new Decimal( - historicalData.data.unlocked[pos - 1], - ); + const prevLockedBalance = new Decimal(historicalData.data.locked[pos - 1]); + const prevUnlockedBalance = new Decimal(historicalData.data.unlocked[pos - 1]); amount = amount.minus(prevBalance); lockedAmount = lockedAmount.minus(prevLockedBalance); diff --git a/api/src/ol/network-addresses.spec.ts b/api/src/ol/network-addresses.spec.ts index a311fdd..39666ec 100644 --- a/api/src/ol/network-addresses.spec.ts +++ b/api/src/ol/network-addresses.spec.ts @@ -1,180 +1,180 @@ -import { NetworkAddresses } from "./network-addresses.js"; +import { NetworkAddresses } from './network-addresses.js'; -describe("network-addresses", () => { - it("deserialize network addresses", () => { +describe('network-addresses', () => { + it('deserialize network addresses', () => { const addresses = [ [ - "012d0400b98789360524180720517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b0800", - "/ip4/185.135.137.54/tcp/6180/noise-ik/0x517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b/handshake/0", + '012d0400b98789360524180720517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b0800', + '/ip4/185.135.137.54/tcp/6180/noise-ik/0x517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b/handshake/0', ], [ - "012d040049b5733505261807203dbb2cc9a7c17072b18ed4aa31c806acfbc0ad92da934a2e1338a715e7473d480800", - "/ip4/73.181.115.53/tcp/6182/noise-ik/0x3dbb2cc9a7c17072b18ed4aa31c806acfbc0ad92da934a2e1338a715e7473d48/handshake/0", + '012d040049b5733505261807203dbb2cc9a7c17072b18ed4aa31c806acfbc0ad92da934a2e1338a715e7473d480800', + '/ip4/73.181.115.53/tcp/6182/noise-ik/0x3dbb2cc9a7c17072b18ed4aa31c806acfbc0ad92da934a2e1338a715e7473d48/handshake/0', ], [ - "012d0400416c0ff0052618072015878523172483c401cc6a674d9a127586db107cf062b113661f6b8521fccf160800", - "/ip4/65.108.15.240/tcp/6182/noise-ik/0x15878523172483c401cc6a674d9a127586db107cf062b113661f6b8521fccf16/handshake/0", + '012d0400416c0ff0052618072015878523172483c401cc6a674d9a127586db107cf062b113661f6b8521fccf160800', + '/ip4/65.108.15.240/tcp/6182/noise-ik/0x15878523172483c401cc6a674d9a127586db107cf062b113661f6b8521fccf16/handshake/0', ], [ - "012d0400416d1e0e05241807207ffc26fff0b0ddd863a0d82f0fd5dfb6f42ea9bf172e41d46e1227f550f7173c0800", - "/ip4/65.109.30.14/tcp/6180/noise-ik/0x7ffc26fff0b0ddd863a0d82f0fd5dfb6f42ea9bf172e41d46e1227f550f7173c/handshake/0", + '012d0400416d1e0e05241807207ffc26fff0b0ddd863a0d82f0fd5dfb6f42ea9bf172e41d46e1227f550f7173c0800', + '/ip4/65.109.30.14/tcp/6180/noise-ik/0x7ffc26fff0b0ddd863a0d82f0fd5dfb6f42ea9bf172e41d46e1227f550f7173c/handshake/0', ], [ - "012d0400905b69ee0524180720f05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b83253480800", - "/ip4/144.91.105.238/tcp/6180/noise-ik/0xf05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b8325348/handshake/0", + '012d0400905b69ee0524180720f05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b83253480800', + '/ip4/144.91.105.238/tcp/6180/noise-ik/0xf05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b8325348/handshake/0', ], [ - "012d0400905b69ee0524180720f05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b83253480800", - "/ip4/144.91.105.238/tcp/6180/noise-ik/0xf05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b8325348/handshake/0", + '012d0400905b69ee0524180720f05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b83253480800', + '/ip4/144.91.105.238/tcp/6180/noise-ik/0xf05b2fbbf1759966ac4ee5386ebdae5cc7740d22eacef75a394f60c3b8325348/handshake/0', ], [ - "012d0400416d15e605241807201c6e935daa380576bc9fb1984ec7725009e121f23954a80300f6097e163c55070800", - "/ip4/65.109.21.230/tcp/6180/noise-ik/0x1c6e935daa380576bc9fb1984ec7725009e121f23954a80300f6097e163c5507/handshake/0", + '012d0400416d15e605241807201c6e935daa380576bc9fb1984ec7725009e121f23954a80300f6097e163c55070800', + '/ip4/65.109.21.230/tcp/6180/noise-ik/0x1c6e935daa380576bc9fb1984ec7725009e121f23954a80300f6097e163c5507/handshake/0', ], - ["00", ""], + ['00', ''], [ - "012d0400416d4f7505241807201870dcf5e8e0924ab284dce9f758d53807e22f2747a9e46708daf921e671342c0800", - "/ip4/65.109.79.117/tcp/6180/noise-ik/0x1870dcf5e8e0924ab284dce9f758d53807e22f2747a9e46708daf921e671342c/handshake/0", + '012d0400416d4f7505241807201870dcf5e8e0924ab284dce9f758d53807e22f2747a9e46708daf921e671342c0800', + '/ip4/65.109.79.117/tcp/6180/noise-ik/0x1870dcf5e8e0924ab284dce9f758d53807e22f2747a9e46708daf921e671342c/handshake/0', ], [ - "012d0400416d5ff005241807209d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d9650800", - "/ip4/65.109.95.240/tcp/6180/noise-ik/0x9d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d965/handshake/0", + '012d0400416d5ff005241807209d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d9650800', + '/ip4/65.109.95.240/tcp/6180/noise-ik/0x9d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d965/handshake/0', ], [ - "012d0400416d5ff005241807209d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d9650800", - "/ip4/65.109.95.240/tcp/6180/noise-ik/0x9d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d965/handshake/0", + '012d0400416d5ff005241807209d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d9650800', + '/ip4/65.109.95.240/tcp/6180/noise-ik/0x9d1ea24b7b2d8fc871f47845ccb0ea91bd56c5665e908965ee56a42eafd8d965/handshake/0', ], [ - "012d040052a5fa4205241807206c3f93028522845daaecf17d2c2a1359473435d9491b593e39a8c839371de03e0800", - "/ip4/82.165.250.66/tcp/6180/noise-ik/0x6c3f93028522845daaecf17d2c2a1359473435d9491b593e39a8c839371de03e/handshake/0", + '012d040052a5fa4205241807206c3f93028522845daaecf17d2c2a1359473435d9491b593e39a8c839371de03e0800', + '/ip4/82.165.250.66/tcp/6180/noise-ik/0x6c3f93028522845daaecf17d2c2a1359473435d9491b593e39a8c839371de03e/handshake/0', ], [ - "012d04005e8216560525180720bc2d1a55f90dfd27e4ef871285f13386997aecf609a3c4d4c4527efc9b2d193e0800", - "/ip4/94.130.22.86/tcp/6181/noise-ik/0xbc2d1a55f90dfd27e4ef871285f13386997aecf609a3c4d4c4527efc9b2d193e/handshake/0", + '012d04005e8216560525180720bc2d1a55f90dfd27e4ef871285f13386997aecf609a3c4d4c4527efc9b2d193e0800', + '/ip4/94.130.22.86/tcp/6181/noise-ik/0xbc2d1a55f90dfd27e4ef871285f13386997aecf609a3c4d4c4527efc9b2d193e/handshake/0', ], [ - "012d04005e8221a605241807207cdc99035e203f58a97ad98431c1657d5e175bee47c2a26ddaa4426a306ca64d0800", - "/ip4/94.130.33.166/tcp/6180/noise-ik/0x7cdc99035e203f58a97ad98431c1657d5e175bee47c2a26ddaa4426a306ca64d/handshake/0", + '012d04005e8221a605241807207cdc99035e203f58a97ad98431c1657d5e175bee47c2a26ddaa4426a306ca64d0800', + '/ip4/94.130.33.166/tcp/6180/noise-ik/0x7cdc99035e203f58a97ad98431c1657d5e175bee47c2a26ddaa4426a306ca64d/handshake/0', ], [ - "012d0400416d24be052418072018ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e27200800", - "/ip4/65.109.36.190/tcp/6180/noise-ik/0x18ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e2720/handshake/0", + '012d0400416d24be052418072018ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e27200800', + '/ip4/65.109.36.190/tcp/6180/noise-ik/0x18ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e2720/handshake/0', ], [ - "012d0400416d24be052418072018ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e27200800", - "/ip4/65.109.36.190/tcp/6180/noise-ik/0x18ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e2720/handshake/0", + '012d0400416d24be052418072018ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e27200800', + '/ip4/65.109.36.190/tcp/6180/noise-ik/0x18ffaa68856dbfc08ed730e593b8b36d86895c48c336df662a0932c9fc0e2720/handshake/0', ], [ - "012d04004014240e0525180720a958d88745c181a28d0323cb0f12a6bc620cdc3694ef3a2dc1873854ee8185500800", - "/ip4/64.20.36.14/tcp/6181/noise-ik/0xa958d88745c181a28d0323cb0f12a6bc620cdc3694ef3a2dc1873854ee818550/handshake/0", + '012d04004014240e0525180720a958d88745c181a28d0323cb0f12a6bc620cdc3694ef3a2dc1873854ee8185500800', + '/ip4/64.20.36.14/tcp/6181/noise-ik/0xa958d88745c181a28d0323cb0f12a6bc620cdc3694ef3a2dc1873854ee818550/handshake/0', ], [ - "012d04004217e8560524180720715142054da65e86c36c6c258a2977e21716b810657a2d03c7a7127655c575480800", - "/ip4/66.23.232.86/tcp/6180/noise-ik/0x715142054da65e86c36c6c258a2977e21716b810657a2d03c7a7127655c57548/handshake/0", + '012d04004217e8560524180720715142054da65e86c36c6c258a2977e21716b810657a2d03c7a7127655c575480800', + '/ip4/66.23.232.86/tcp/6180/noise-ik/0x715142054da65e86c36c6c258a2977e21716b810657a2d03c7a7127655c57548/handshake/0', ], [ - "012d04006391c8620524180720836219f8733d0c2663c0164568e079dcc0a1e6b24ab5c8bc09e157f17101a2020800", - "/ip4/99.145.200.98/tcp/6180/noise-ik/0x836219f8733d0c2663c0164568e079dcc0a1e6b24ab5c8bc09e157f17101a202/handshake/0", + '012d04006391c8620524180720836219f8733d0c2663c0164568e079dcc0a1e6b24ab5c8bc09e157f17101a2020800', + '/ip4/99.145.200.98/tcp/6180/noise-ik/0x836219f8733d0c2663c0164568e079dcc0a1e6b24ab5c8bc09e157f17101a202/handshake/0', ], [ - "012d040043d93133052418072034b1af0034528b73f7c8cb1ce345f9c4f6818d8b920b00c5ab78e473594ad2720800", - "/ip4/67.217.49.51/tcp/6180/noise-ik/0x34b1af0034528b73f7c8cb1ce345f9c4f6818d8b920b00c5ab78e473594ad272/handshake/0", + '012d040043d93133052418072034b1af0034528b73f7c8cb1ce345f9c4f6818d8b920b00c5ab78e473594ad2720800', + '/ip4/67.217.49.51/tcp/6180/noise-ik/0x34b1af0034528b73f7c8cb1ce345f9c4f6818d8b920b00c5ab78e473594ad272/handshake/0', ], [ - "012d0400bca617120524180720cd8b02613e3cc219d21e31ec53c8aba3568ac7e4b3640fc96b38614636a74e740800", - "/ip4/188.166.23.18/tcp/6180/noise-ik/0xcd8b02613e3cc219d21e31ec53c8aba3568ac7e4b3640fc96b38614636a74e74/handshake/0", + '012d0400bca617120524180720cd8b02613e3cc219d21e31ec53c8aba3568ac7e4b3640fc96b38614636a74e740800', + '/ip4/188.166.23.18/tcp/6180/noise-ik/0xcd8b02613e3cc219d21e31ec53c8aba3568ac7e4b3640fc96b38614636a74e74/handshake/0', ], [ - "012d040052df13880524180720ce7168ac62fde698c34d2e7b968f8b7b4be7cce221829dbcc3761fdf1aefaf700800", - "/ip4/82.223.19.136/tcp/6180/noise-ik/0xce7168ac62fde698c34d2e7b968f8b7b4be7cce221829dbcc3761fdf1aefaf70/handshake/0", + '012d040052df13880524180720ce7168ac62fde698c34d2e7b968f8b7b4be7cce221829dbcc3761fdf1aefaf700800', + '/ip4/82.223.19.136/tcp/6180/noise-ik/0xce7168ac62fde698c34d2e7b968f8b7b4be7cce221829dbcc3761fdf1aefaf70/handshake/0', ], [ - "012d0400339f64d905251807205227b4c0f58f8ccdade360975ebd134c3546e2d581561360e25b45f1b531ce270800", - "/ip4/51.159.100.217/tcp/6181/noise-ik/0x5227b4c0f58f8ccdade360975ebd134c3546e2d581561360e25b45f1b531ce27/handshake/0", + '012d0400339f64d905251807205227b4c0f58f8ccdade360975ebd134c3546e2d581561360e25b45f1b531ce270800', + '/ip4/51.159.100.217/tcp/6181/noise-ik/0x5227b4c0f58f8ccdade360975ebd134c3546e2d581561360e25b45f1b531ce27/handshake/0', ], [ - "012d0400339f651f0524180720bbc8ed72f985c8bd4ba546829432595b83c53e568f31c0d1775636f3b177ec090800", - "/ip4/51.159.101.31/tcp/6180/noise-ik/0xbbc8ed72f985c8bd4ba546829432595b83c53e568f31c0d1775636f3b177ec09/handshake/0", + '012d0400339f651f0524180720bbc8ed72f985c8bd4ba546829432595b83c53e568f31c0d1775636f3b177ec090800', + '/ip4/51.159.101.31/tcp/6180/noise-ik/0xbbc8ed72f985c8bd4ba546829432595b83c53e568f31c0d1775636f3b177ec09/handshake/0', ], [ - "012d0400416d1e370524180720e41c5b51f18d9826ca23136f0edc442d701c82c246ed1f306ece495e763df8230800", - "/ip4/65.109.30.55/tcp/6180/noise-ik/0xe41c5b51f18d9826ca23136f0edc442d701c82c246ed1f306ece495e763df823/handshake/0", + '012d0400416d1e370524180720e41c5b51f18d9826ca23136f0edc442d701c82c246ed1f306ece495e763df8230800', + '/ip4/65.109.30.55/tcp/6180/noise-ik/0xe41c5b51f18d9826ca23136f0edc442d701c82c246ed1f306ece495e763df823/handshake/0', ], [ - "012d040058c633fa052518072073b3cf80bb90042fcecd19c533cdbf3400521c5ef1d564603b9e749182abb7740800", - "/ip4/88.198.51.250/tcp/6181/noise-ik/0x73b3cf80bb90042fcecd19c533cdbf3400521c5ef1d564603b9e749182abb774/handshake/0", + '012d040058c633fa052518072073b3cf80bb90042fcecd19c533cdbf3400521c5ef1d564603b9e749182abb7740800', + '/ip4/88.198.51.250/tcp/6181/noise-ik/0x73b3cf80bb90042fcecd19c533cdbf3400521c5ef1d564603b9e749182abb774/handshake/0', ], [ - "012d040058c633f90524180720ae048b6866487aad7563376e201e7fa7056e97e25f9b11d9f1dd217f1813dd730800", - "/ip4/88.198.51.249/tcp/6180/noise-ik/0xae048b6866487aad7563376e201e7fa7056e97e25f9b11d9f1dd217f1813dd73/handshake/0", + '012d040058c633f90524180720ae048b6866487aad7563376e201e7fa7056e97e25f9b11d9f1dd217f1813dd730800', + '/ip4/88.198.51.249/tcp/6180/noise-ik/0xae048b6866487aad7563376e201e7fa7056e97e25f9b11d9f1dd217f1813dd73/handshake/0', ], [ - "012d040068f85ec305251807209ac157ee324e4129c9edac7ba5eca70af299929ae8c0d7362f4e6c75a7ac447e0800", - "/ip4/104.248.94.195/tcp/6181/noise-ik/0x9ac157ee324e4129c9edac7ba5eca70af299929ae8c0d7362f4e6c75a7ac447e/handshake/0", + '012d040068f85ec305251807209ac157ee324e4129c9edac7ba5eca70af299929ae8c0d7362f4e6c75a7ac447e0800', + '/ip4/104.248.94.195/tcp/6181/noise-ik/0x9ac157ee324e4129c9edac7ba5eca70af299929ae8c0d7362f4e6c75a7ac447e/handshake/0', ], [ - "012d04009f41cb8a0524180720c1954be575981591f2eabd67913c839657ac45a866b15bff40b5d80eb4d827780800", - "/ip4/159.65.203.138/tcp/6180/noise-ik/0xc1954be575981591f2eabd67913c839657ac45a866b15bff40b5d80eb4d82778/handshake/0", + '012d04009f41cb8a0524180720c1954be575981591f2eabd67913c839657ac45a866b15bff40b5d80eb4d827780800', + '/ip4/159.65.203.138/tcp/6180/noise-ik/0xc1954be575981591f2eabd67913c839657ac45a866b15bff40b5d80eb4d82778/handshake/0', ], [ - "012d04009edc7b210524180720ecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b0800", - "/ip4/158.220.123.33/tcp/6180/noise-ik/0xecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b/handshake/0", + '012d04009edc7b210524180720ecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b0800', + '/ip4/158.220.123.33/tcp/6180/noise-ik/0xecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b/handshake/0', ], [ - "012d04009edc7b210524180720ecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b0800", - "/ip4/158.220.123.33/tcp/6180/noise-ik/0xecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b/handshake/0", + '012d04009edc7b210524180720ecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b0800', + '/ip4/158.220.123.33/tcp/6180/noise-ik/0xecade13ac99d1e085453382e2d6965453906daa380126198ea2006c5708ca23b/handshake/0', ], [ - "012d0400de651ff20525180720dcab287b256bb1e90cda2537553ee19cac195ce67c2fefc7ff25b8aaf2368e6d0800", - "/ip4/222.101.31.242/tcp/6181/noise-ik/0xdcab287b256bb1e90cda2537553ee19cac195ce67c2fefc7ff25b8aaf2368e6d/handshake/0", + '012d0400de651ff20525180720dcab287b256bb1e90cda2537553ee19cac195ce67c2fefc7ff25b8aaf2368e6d0800', + '/ip4/222.101.31.242/tcp/6181/noise-ik/0xdcab287b256bb1e90cda2537553ee19cac195ce67c2fefc7ff25b8aaf2368e6d/handshake/0', ], [ - "012d040088f4473f052418072085bdd9240cae48f24e6bfbc440f878a99a9d29e2113773d089808221b96f397d0800", - "/ip4/136.244.71.63/tcp/6180/noise-ik/0x85bdd9240cae48f24e6bfbc440f878a99a9d29e2113773d089808221b96f397d/handshake/0", + '012d040088f4473f052418072085bdd9240cae48f24e6bfbc440f878a99a9d29e2113773d089808221b96f397d0800', + '/ip4/136.244.71.63/tcp/6180/noise-ik/0x85bdd9240cae48f24e6bfbc440f878a99a9d29e2113773d089808221b96f397d/handshake/0', ], [ - "012d0400ac68d3080525180720c04300abebc472cdea0e1f7160ece6c09e49145c2b5b72318bdc43c11aceb2030800", - "/ip4/172.104.211.8/tcp/6181/noise-ik/0xc04300abebc472cdea0e1f7160ece6c09e49145c2b5b72318bdc43c11aceb203/handshake/0", + '012d0400ac68d3080525180720c04300abebc472cdea0e1f7160ece6c09e49145c2b5b72318bdc43c11aceb2030800', + '/ip4/172.104.211.8/tcp/6181/noise-ik/0xc04300abebc472cdea0e1f7160ece6c09e49145c2b5b72318bdc43c11aceb203/handshake/0', ], [ - "012d0400ac68d6fe0524180720481b1c5f30c472b1463445f6e9b8ed517cab75e2c3775ac2704db55c89df7a190800", - "/ip4/172.104.214.254/tcp/6180/noise-ik/0x481b1c5f30c472b1463445f6e9b8ed517cab75e2c3775ac2704db55c89df7a19/handshake/0", + '012d0400ac68d6fe0524180720481b1c5f30c472b1463445f6e9b8ed517cab75e2c3775ac2704db55c89df7a190800', + '/ip4/172.104.214.254/tcp/6180/noise-ik/0x481b1c5f30c472b1463445f6e9b8ed517cab75e2c3775ac2704db55c89df7a19/handshake/0', ], [ - "012d0400460ff20605261807201017ce1abc30e356660b8b0542275f2fb4373b5f8a82b7800a5b3fdf718ae55f0800", - "/ip4/70.15.242.6/tcp/6182/noise-ik/0x1017ce1abc30e356660b8b0542275f2fb4373b5f8a82b7800a5b3fdf718ae55f/handshake/0", + '012d0400460ff20605261807201017ce1abc30e356660b8b0542275f2fb4373b5f8a82b7800a5b3fdf718ae55f0800', + '/ip4/70.15.242.6/tcp/6182/noise-ik/0x1017ce1abc30e356660b8b0542275f2fb4373b5f8a82b7800a5b3fdf718ae55f/handshake/0', ], [ - "012d0400ccba4a2c05241807203c37c7d6a5122a6b9ef07a11cc40e445874eb0841ae028d6326bf67768cce2350800", - "/ip4/204.186.74.44/tcp/6180/noise-ik/0x3c37c7d6a5122a6b9ef07a11cc40e445874eb0841ae028d6326bf67768cce235/handshake/0", + '012d0400ccba4a2c05241807203c37c7d6a5122a6b9ef07a11cc40e445874eb0841ae028d6326bf67768cce2350800', + '/ip4/204.186.74.44/tcp/6180/noise-ik/0x3c37c7d6a5122a6b9ef07a11cc40e445874eb0841ae028d6326bf67768cce235/handshake/0', ], [ - "012d0400bc286b6905241807201691a108495c157d4aaa0c95b7b3f704ca5d2aca7fe359ff82e435733e3216030800", - "/ip4/188.40.107.105/tcp/6180/noise-ik/0x1691a108495c157d4aaa0c95b7b3f704ca5d2aca7fe359ff82e435733e321603/handshake/0", + '012d0400bc286b6905241807201691a108495c157d4aaa0c95b7b3f704ca5d2aca7fe359ff82e435733e3216030800', + '/ip4/188.40.107.105/tcp/6180/noise-ik/0x1691a108495c157d4aaa0c95b7b3f704ca5d2aca7fe359ff82e435733e321603/handshake/0', ], [ - "012d0400416d50b30525180720619898b2f99fba7b25fae35e3eab03164d7d9ce0d10abe8f6ceae9a43ffa1c340800", - "/ip4/65.109.80.179/tcp/6181/noise-ik/0x619898b2f99fba7b25fae35e3eab03164d7d9ce0d10abe8f6ceae9a43ffa1c34/handshake/0", + '012d0400416d50b30525180720619898b2f99fba7b25fae35e3eab03164d7d9ce0d10abe8f6ceae9a43ffa1c340800', + '/ip4/65.109.80.179/tcp/6181/noise-ik/0x619898b2f99fba7b25fae35e3eab03164d7d9ce0d10abe8f6ceae9a43ffa1c34/handshake/0', ], [ - "012d040033516da8052418072072f8b945ed995dda94212fdc0a8e90a66972053fe0e1c7b4481b2cf512d7615a0800", - "/ip4/51.81.109.168/tcp/6180/noise-ik/0x72f8b945ed995dda94212fdc0a8e90a66972053fe0e1c7b4481b2cf512d7615a/handshake/0", + '012d040033516da8052418072072f8b945ed995dda94212fdc0a8e90a66972053fe0e1c7b4481b2cf512d7615a0800', + '/ip4/51.81.109.168/tcp/6180/noise-ik/0x72f8b945ed995dda94212fdc0a8e90a66972053fe0e1c7b4481b2cf512d7615a/handshake/0', ], [ - "012d04009b8516c80525180720cdb76ab0742af133792c61ec111805eb64b3d07b4dacea5fd1fefcf3d05a9c1e0800", - "/ip4/155.133.22.200/tcp/6181/noise-ik/0xcdb76ab0742af133792c61ec111805eb64b3d07b4dacea5fd1fefcf3d05a9c1e/handshake/0", + '012d04009b8516c80525180720cdb76ab0742af133792c61ec111805eb64b3d07b4dacea5fd1fefcf3d05a9c1e0800', + '/ip4/155.133.22.200/tcp/6181/noise-ik/0xcdb76ab0742af133792c61ec111805eb64b3d07b4dacea5fd1fefcf3d05a9c1e/handshake/0', ], [ - "012d0400b98789360524180720517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b0800", - "/ip4/185.135.137.54/tcp/6180/noise-ik/0x517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b/handshake/0", + '012d0400b98789360524180720517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b0800', + '/ip4/185.135.137.54/tcp/6180/noise-ik/0x517cb4962e45afb8f66ced74773967b716b96aa44c2956799355495b51bbe71b/handshake/0', ], ]; for (const [encoded, decoded] of addresses) { - const bytes = Buffer.from(encoded, "hex"); + const bytes = Buffer.from(encoded, 'hex'); const networkAddresses = NetworkAddresses.fromBytes(bytes); - expect(networkAddresses?.toString() ?? "").toBe(decoded); + expect(networkAddresses?.toString() ?? '').toBe(decoded); } }); }); diff --git a/api/src/ol/network-addresses.ts b/api/src/ol/network-addresses.ts index 5175ac4..d24c00e 100644 --- a/api/src/ol/network-addresses.ts +++ b/api/src/ol/network-addresses.ts @@ -1,4 +1,4 @@ -import { BCS } from "aptos"; +import { BCS } from 'aptos'; const PUBLIC_KEY_SIZE = 32; @@ -29,14 +29,13 @@ class Ip6Protocol implements INetworkProtocol { } public toString(): string { - const ip = Array.from(this.ip).map((it) => it.toString(16)); + const ip = Array.from(this.ip).map((it) => it.toString(16)); return `/ip6/${ip.join(':')}`; } } class DnsProtocol implements INetworkProtocol { - public constructor(public readonly name: string) { - } + public constructor(public readonly name: string) {} public toString(): string { return `/dns/${this.name}`; @@ -45,7 +44,7 @@ class DnsProtocol implements INetworkProtocol { class TcpProtocol implements INetworkProtocol { public constructor(public readonly port: number) { - if (port < 0 || port > 0xFFFF) { + if (port < 0 || port > 0xffff) { throw new Error('invalid port'); } } @@ -70,7 +69,7 @@ class NoiseIKProtocol implements INetworkProtocol { class HandshakeProtocol implements INetworkProtocol { public constructor(public readonly version: number) { - if (version < 0 || version > 0xFF) { + if (version < 0 || version > 0xff) { throw new Error('invalid length'); } } @@ -93,25 +92,29 @@ type ProtocolDeserializer = (deserializer: BCS.Deserializer) => NetworkProtocol; export class NetworkAddresses { private static protocolDeserializers: ProtocolDeserializer[] = [ function ip4(deserializer: BCS.Deserializer): Ip4Protocol { - return new Ip4Protocol(new Uint8Array([ - deserializer.deserializeU8(), - deserializer.deserializeU8(), - deserializer.deserializeU8(), - deserializer.deserializeU8(), - ])); + return new Ip4Protocol( + new Uint8Array([ + deserializer.deserializeU8(), + deserializer.deserializeU8(), + deserializer.deserializeU8(), + deserializer.deserializeU8(), + ]), + ); }, function ip6(deserializer: BCS.Deserializer): Ip6Protocol { - return new Ip6Protocol(new Uint16Array([ - deserializer.deserializeU16(), - deserializer.deserializeU16(), - deserializer.deserializeU16(), - deserializer.deserializeU16(), - deserializer.deserializeU16(), - deserializer.deserializeU16(), - deserializer.deserializeU16(), - deserializer.deserializeU16(), - ])); + return new Ip6Protocol( + new Uint16Array([ + deserializer.deserializeU16(), + deserializer.deserializeU16(), + deserializer.deserializeU16(), + deserializer.deserializeU16(), + deserializer.deserializeU16(), + deserializer.deserializeU16(), + deserializer.deserializeU16(), + deserializer.deserializeU16(), + ]), + ); }, function dns(deserializer: BCS.Deserializer) { @@ -181,4 +184,4 @@ export class NetworkAddresses { public toString(): string { return this.protocols.map((protocol) => protocol.toString()).join(''); } -} \ No newline at end of file +} diff --git a/api/src/ol/ol-clickhouse-ingestor.processor.ts b/api/src/ol/ol-clickhouse-ingestor.processor.ts index 8929598..d14c4e6 100644 --- a/api/src/ol/ol-clickhouse-ingestor.processor.ts +++ b/api/src/ol/ol-clickhouse-ingestor.processor.ts @@ -1,27 +1,24 @@ -import pathUtil from "node:path"; -import fs from "node:fs"; -import { execFile as execFileNative } from "node:child_process"; -import util from "node:util"; -import { Readable } from "node:stream"; +import pathUtil from 'node:path'; +import fs from 'node:fs'; +import { execFile as execFileNative } from 'node:child_process'; +import util from 'node:util'; +import { Readable } from 'node:stream'; -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import _ from "lodash"; -import { OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import _ from 'lodash'; +import { OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; -import { S3Service } from "../s3/s3.service.js"; -import { cleanUp, createTmpDir } from "../utils.js"; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; +import { S3Service } from '../s3/s3.service.js'; +import { cleanUp, createTmpDir } from '../utils.js'; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; const execFile = util.promisify(execFileNative); -@Processor("ol-clickhouse-ingestor") -export class OlClickhouseIngestorProcessor - extends WorkerHost - implements OnModuleInit -{ +@Processor('ol-clickhouse-ingestor') +export class OlClickhouseIngestorProcessor extends WorkerHost implements OnModuleInit { public constructor( - @InjectQueue("ol-clickhouse-ingestor") + @InjectQueue('ol-clickhouse-ingestor') private readonly olClickhouseIngestor: Queue, private readonly s3Service: S3Service, @@ -43,11 +40,11 @@ export class OlClickhouseIngestorProcessor return new Promise((resolve, reject) => { const file = fs.createWriteStream(dest); - file.on("close", () => { + file.on('close', () => { resolve(dest); }); - file.on("error", (err) => { + file.on('error', (err) => { console.error(err); reject(err); }); @@ -57,7 +54,7 @@ export class OlClickhouseIngestorProcessor } public async onModuleInit() { - await this.olClickhouseIngestor.add("getMissingFiles", undefined, { + await this.olClickhouseIngestor.add('getMissingFiles', undefined, { repeat: { every: 30 * 60 * 1_000, // 30 minutes }, @@ -66,11 +63,11 @@ export class OlClickhouseIngestorProcessor public async process(job: Job) { switch (job.name) { - case "getMissingFiles": + case 'getMissingFiles': await this.getMissingFiles(); break; - case "ingest": + case 'ingest': await this.ingest(job.data.file); break; @@ -82,7 +79,7 @@ export class OlClickhouseIngestorProcessor private async getIngestedFiles(): Promise { const resultSet = await this.clickhouseService.client.query({ query: 'SELECT * FROM "ingested_files"', - format: "JSONEachRow", + format: 'JSONEachRow', }); const dataset = await resultSet.json<{ name: string }>(); return _.uniq(dataset.map((it) => it.name)); @@ -90,13 +87,13 @@ export class OlClickhouseIngestorProcessor private async getMissingFiles() { const ingestedFile = await this.getIngestedFiles(); - const parquetFiles = await this.s3Service.listFiles("parquets/"); + const parquetFiles = await this.s3Service.listFiles('parquets/'); const missingFiles = _.difference(parquetFiles, ingestedFile); await this.olClickhouseIngestor.addBulk( missingFiles.map((file) => ({ - name: "ingest", + name: 'ingest', data: { file, }, @@ -115,17 +112,15 @@ export class OlClickhouseIngestorProcessor const files = await fs.promises.readdir(archiveDir); for (const file of files) { - if (file.endsWith(".parquet")) { - await this.clickhouseService.insertParquetFile( - pathUtil.join(archiveDir, file), - ); + if (file.endsWith('.parquet')) { + await this.clickhouseService.insertParquetFile(pathUtil.join(archiveDir, file)); } } await this.clickhouseService.client.insert({ - table: "ingested_files", + table: 'ingested_files', values: [{ name: file }], - format: "JSONEachRow", + format: 'JSONEachRow', }); await cleanUp(archiveDir); @@ -135,6 +130,6 @@ export class OlClickhouseIngestorProcessor const filename = pathUtil.basename(filepath); const dirnname = pathUtil.dirname(filepath); - await execFile("tar", ["xf", filename], { cwd: dirnname }); + await execFile('tar', ['xf', filename], { cwd: dirnname }); } } diff --git a/api/src/ol/ol-parquet-producer.processor.ts b/api/src/ol/ol-parquet-producer.processor.ts index ea143e6..e2a3ea2 100644 --- a/api/src/ol/ol-parquet-producer.processor.ts +++ b/api/src/ol/ol-parquet-producer.processor.ts @@ -5,7 +5,7 @@ import { execFile as execFileNative } from 'node:child_process'; import util from 'node:util'; import { Readable } from 'node:stream'; -import Bluebird from "bluebird"; +import Bluebird from 'bluebird'; import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; import _ from 'lodash'; import { OnModuleInit } from '@nestjs/common'; @@ -19,10 +19,7 @@ const execFile = util.promisify(execFileNative); const PARQUETS_DIR = 'parquets'; @Processor('ol-parquet-producer') -export class OlParquetProducerProcessor - extends WorkerHost - implements OnModuleInit -{ +export class OlParquetProducerProcessor extends WorkerHost implements OnModuleInit { public constructor( @InjectQueue('ol-parquet-producer') private readonly olParquetProducerQueue: Queue, @@ -54,7 +51,7 @@ export class OlParquetProducerProcessor // 20m timeout to avoid blocking the queue Bluebird.delay(20 * 60 * 1_000).then(() => { - throw new Error("timeout"); + throw new Error('timeout'); }), ]); break; @@ -73,9 +70,7 @@ export class OlParquetProducerProcessor (it) => it.split('/')[0], ); - const missing = _.difference(json, parquets).map((it) => - parseInt(it.split('-')[0], 10), - ); + const missing = _.difference(json, parquets).map((it) => parseInt(it.split('-')[0], 10)); if (missing.length) { await this.olParquetProducerQueue.addBulk( @@ -112,20 +107,12 @@ export class OlParquetProducerProcessor const parquetDir = await this.transformerService.transform(transactionsFiles); let files = await fs.promises.readdir(parquetDir); - files = files.filter( - (it) => it.substring(it.length - '.parquet'.length) === '.parquet', - ); + files = files.filter((it) => it.substring(it.length - '.parquet'.length) === '.parquet'); job.log(`compressing (${start}-${end}tar.gz) ...`); - await execFile( - `tar`, - [ - 'czf', - `${start}-${end}.tar.gz`, - ...files, - ], - { cwd: parquetDir }, - ); + await execFile(`tar`, ['czf', `${start}-${end}.tar.gz`, ...files], { + cwd: parquetDir, + }); job.log('uploading'); await this.s3Service.upload( @@ -139,9 +126,7 @@ export class OlParquetProducerProcessor private async download(key: string): Promise { const res = await this.s3Service.download(key); - const tmpDir = await fs.promises.mkdtemp( - pathUtil.join(os.tmpdir(), 'parquet-producer-'), - ); + const tmpDir = await fs.promises.mkdtemp(pathUtil.join(os.tmpdir(), 'parquet-producer-')); const dest = pathUtil.join(tmpDir, pathUtil.basename(key)); return new Promise((resolve, reject) => { @@ -164,4 +149,4 @@ export class OlParquetProducerProcessor await execFile('tar', ['xf', filepath], { cwd: dest }); return dest; } -} \ No newline at end of file +} diff --git a/api/src/ol/ol-version-batch.processor.ts b/api/src/ol/ol-version-batch.processor.ts index a334b03..762d519 100644 --- a/api/src/ol/ol-version-batch.processor.ts +++ b/api/src/ol/ol-version-batch.processor.ts @@ -1,16 +1,16 @@ -import os from "node:os"; -import path, { basename } from "node:path"; -import util from "node:util"; -import { execFile as execFileNative } from "node:child_process"; -import { writeFile, mkdir, mkdtemp } from "node:fs/promises"; -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import { OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; - -import { OlService } from "./ol.service.js"; -import { cleanUp } from "../utils.js"; -import { S3Service } from "../s3/s3.service.js"; -import { NotPendingTransaction } from "./types.js"; +import os from 'node:os'; +import path, { basename } from 'node:path'; +import util from 'node:util'; +import { execFile as execFileNative } from 'node:child_process'; +import { writeFile, mkdir, mkdtemp } from 'node:fs/promises'; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import { OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; + +import { OlService } from './ol.service.js'; +import { cleanUp } from '../utils.js'; +import { S3Service } from '../s3/s3.service.js'; +import { NotPendingTransaction } from './types.js'; const execFile = util.promisify(execFileNative); @@ -18,11 +18,8 @@ export interface VersionBatchJobData { index: number; } -@Processor("ol-version-batch") -export class OlVersionBatchProcessor - extends WorkerHost - implements OnModuleInit -{ +@Processor('ol-version-batch') +export class OlVersionBatchProcessor extends WorkerHost implements OnModuleInit { public static TRANSACTIONS_PER_REQUEST = 100; public static BATCH_SIZE = 100; @@ -30,10 +27,8 @@ export class OlVersionBatchProcessor public static isLastVersionOfBatch(version: number): boolean { return ( version % - (OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST * - OlVersionBatchProcessor.BATCH_SIZE) === - (OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST - 1) * - OlVersionBatchProcessor.BATCH_SIZE + (OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST * OlVersionBatchProcessor.BATCH_SIZE) === + (OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST - 1) * OlVersionBatchProcessor.BATCH_SIZE ); } @@ -48,9 +43,7 @@ export class OlVersionBatchProcessor ); } - private static getMinMax( - transactions: NotPendingTransaction[], - ): [number, number] | null { + private static getMinMax(transactions: NotPendingTransaction[]): [number, number] | null { const { length } = transactions; if (!length) { return null; @@ -75,13 +68,10 @@ export class OlVersionBatchProcessor private static getIndexDir(index: number): string { const from = - index * - OlVersionBatchProcessor.BATCH_SIZE * - OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST; + index * OlVersionBatchProcessor.BATCH_SIZE * OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST; const to = from + - (OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST - 1) * - OlVersionBatchProcessor.BATCH_SIZE; + (OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST - 1) * OlVersionBatchProcessor.BATCH_SIZE; return `${from}-${to}`; } @@ -89,34 +79,30 @@ export class OlVersionBatchProcessor private readonly s3Service: S3Service, private readonly olService: OlService, - @InjectQueue("ol-version-batch") + @InjectQueue('ol-version-batch') private readonly olVersionBatchQueue: Queue, ) { super(); } public async onModuleInit() { - await this.olVersionBatchQueue.add( - "getMissingBatchTransactions", - undefined, - { - repeat: { - every: 30 * 60 * 1_000, // 30 minutes - }, + await this.olVersionBatchQueue.add('getMissingBatchTransactions', undefined, { + repeat: { + every: 30 * 60 * 1_000, // 30 minutes }, - ); + }); } public async process(job: Job) { switch (job.name) { - case "batch": + case 'batch': { const { index } = job.data; await this.processVersionBatch(job, index); } break; - case "getMissingBatchTransactions": + case 'getMissingBatchTransactions': await this.getMissingBatchTransactions(); break; @@ -125,36 +111,25 @@ export class OlVersionBatchProcessor } } - public async processVersionBatch( - job: Job, - index: number, - ) { + public async processVersionBatch(job: Job, index: number) { const archivePath = await this.fetchTransactions(job, index); const dest = `transactions/${path.basename(archivePath)}`; job.log(`uploading ${dest}`); - await this.s3Service.upload( - archivePath, - `transactions/${basename(archivePath)}`, - ); + await this.s3Service.upload(archivePath, `transactions/${basename(archivePath)}`); job.log(`uploaded ${dest}`); await cleanUp(path.dirname(archivePath)); await job.updateProgress(100); } - private async fetchTransactions( - job: Job, - index: number, - ) { + private async fetchTransactions(job: Job, index: number) { const batchMinMax = [-1, -1]; const from = - index * - OlVersionBatchProcessor.BATCH_SIZE * - OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST; + index * OlVersionBatchProcessor.BATCH_SIZE * OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST; const indexDir = OlVersionBatchProcessor.getIndexDir(index); - const tmpDir = await mkdtemp(path.join(os.tmpdir(), "transactions-batch-")); + const tmpDir = await mkdtemp(path.join(os.tmpdir(), 'transactions-batch-')); const destDir = path.join(tmpDir, indexDir); await mkdir(destDir, { recursive: true }); @@ -166,18 +141,16 @@ export class OlVersionBatchProcessor limit: OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST, }); const notPendingTransactions = transactions.filter( - (tx) => tx.type !== "pending_transaction", + (tx) => tx.type !== 'pending_transaction', ) as NotPendingTransaction[]; - if ( - transactions.length !== OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST - ) { - throw new Error("missing transactions"); + if (transactions.length !== OlVersionBatchProcessor.TRANSACTIONS_PER_REQUEST) { + throw new Error('missing transactions'); } const minMax = OlVersionBatchProcessor.getMinMax(notPendingTransactions); if (minMax === null) { - throw new Error("unable to determine min/max"); + throw new Error('unable to determine min/max'); } const [min, max] = minMax; @@ -188,10 +161,7 @@ export class OlVersionBatchProcessor batchMinMax[1] = max; } - await writeFile( - path.join(destDir, `${min}-${max}.json`), - JSON.stringify(transactions), - ); + await writeFile(path.join(destDir, `${min}-${max}.json`), JSON.stringify(transactions)); await job.updateProgress((i * 90) / OlVersionBatchProcessor.BATCH_SIZE); } @@ -205,7 +175,7 @@ export class OlVersionBatchProcessor } private async compress(src: string, dst: string) { - await execFile("tar", ["czf", dst, "."], { cwd: src }); + await execFile('tar', ['czf', dst, '.'], { cwd: src }); } private async getMissingBatchTransactions() { @@ -231,7 +201,7 @@ export class OlVersionBatchProcessor if (indexes.length) { await this.olVersionBatchQueue.addBulk( indexes.map((index) => ({ - name: "batch", + name: 'batch', data: { index: index, }, diff --git a/api/src/ol/ol-version.processor.ts b/api/src/ol/ol-version.processor.ts index 3ee3bcb..16a9565 100644 --- a/api/src/ol/ol-version.processor.ts +++ b/api/src/ol/ol-version.processor.ts @@ -1,31 +1,31 @@ -import os from "node:os"; -import pathUtil from "node:path"; -import fs from "node:fs"; - -import _ from "lodash"; -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import { Inject, OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; -import BN from "bn.js"; -import axios from "axios"; -import Bluebird from "bluebird"; -import { Types as AptosTypes } from "aptos"; -import { JSONCodec } from "nats"; -import { ConfigService } from "@nestjs/config"; -import qs from "qs"; -import { PendingTransactionStatus } from "@prisma/client"; - -import { OlDbService } from "../ol-db/ol-db.service.js"; -import { ClickhouseQueryResponse, ClickhouseService } from "../clickhouse/clickhouse.service.js"; -import { TransformerService } from "./transformer.service.js"; -import { NotPendingTransaction } from "./types.js"; -import { OlConfig } from "../config/config.interface.js"; -import { WalletSubscriptionService } from "../wallet-subscription/wallet-subscription.service.js"; -import { NatsService } from "../nats/nats.service.js"; -import { bnBisect, parseHexString } from "../utils.js"; -import { ITransactionsService } from "./transactions/interfaces.js"; -import { Types } from "../types.js"; -import { OlService } from "./ol.service.js"; +import os from 'node:os'; +import pathUtil from 'node:path'; +import fs from 'node:fs'; + +import _ from 'lodash'; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import { Inject, OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; +import BN from 'bn.js'; +import axios from 'axios'; +import Bluebird from 'bluebird'; +import { Types as AptosTypes } from 'aptos'; +import { JSONCodec } from 'nats'; +import { ConfigService } from '@nestjs/config'; +import qs from 'qs'; +import { PendingTransactionStatus } from '@prisma/client'; + +import { OlDbService } from '../ol-db/ol-db.service.js'; +import { ClickhouseQueryResponse, ClickhouseService } from '../clickhouse/clickhouse.service.js'; +import { TransformerService } from './transformer.service.js'; +import { NotPendingTransaction } from './types.js'; +import { OlConfig } from '../config/config.interface.js'; +import { WalletSubscriptionService } from '../wallet-subscription/wallet-subscription.service.js'; +import { NatsService } from '../nats/nats.service.js'; +import { bnBisect, parseHexString } from '../utils.js'; +import { ITransactionsService } from './transactions/interfaces.js'; +import { Types } from '../types.js'; +import { OlService } from './ol.service.js'; const ZERO = new BN(0); const ONE = new BN(1); @@ -46,14 +46,14 @@ export interface VersionJobData { version: string; } -@Processor("ol-version") +@Processor('ol-version') export class OlVersionProcessor extends WorkerHost implements OnModuleInit { private static jsonCodec = JSONCodec(); private readonly providerHost: string; public constructor( - @InjectQueue("ol-version") + @InjectQueue('ol-version') private readonly olVersionQueue: Queue, configService: ConfigService, @@ -75,12 +75,12 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { ) { super(); - const config = configService.get("ol")!; + const config = configService.get('ol')!; this.providerHost = config.provider; } public async onModuleInit() { - await this.olVersionQueue.add("getMissingVersions", undefined, { + await this.olVersionQueue.add('getMissingVersions', undefined, { repeat: { every: 5 * 1_000, // 5 seconds }, @@ -90,7 +90,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { }, }); - await this.olVersionQueue.add("fetchLatestVersion", undefined, { + await this.olVersionQueue.add('fetchLatestVersion', undefined, { repeat: { every: 5 * 1_000, // 5 seconds }, @@ -100,7 +100,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { }, }); - await this.olVersionQueue.add("updateLatestStableVersion", undefined, { + await this.olVersionQueue.add('updateLatestStableVersion', undefined, { repeat: { every: 5 * 1_000, // 5 seconds }, @@ -113,7 +113,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { public async process(job: Job) { switch (job.name) { - case "getMissingVersions": + case 'getMissingVersions': try { await Promise.race([ this.getMissingVersions(), @@ -125,11 +125,11 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { } break; - case "version": + case 'version': await this.processVersion(job.data.version); break; - case "fetchLatestVersion": + case 'fetchLatestVersion': try { await Promise.race([ this.fetchLatestVersion(), @@ -141,7 +141,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { } break; - case "updateLatestStableVersion": + case 'updateLatestStableVersion': try { await Promise.race([ this.updateLatestStableVersion(), @@ -160,9 +160,9 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { private async updateLatestStableVersion(): Promise { const js = this.natsService.jetstream; - const kv = await js.views.kv("ol"); - const entry = await kv.get("ledger.latestVersion"); - let ledgerLatestVersion = new BN(entry?.string() ?? "0"); + const kv = await js.views.kv('ol'); + const entry = await kv.get('ledger.latestVersion'); + let ledgerLatestVersion = new BN(entry?.string() ?? '0'); while (true) { const start = ledgerLatestVersion; @@ -221,12 +221,11 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { start: start.toString(10), end: end.toString(10), }, - format: "JSONColumnsWithMetadata", + format: 'JSONColumnsWithMetadata', }); - const rows = - (await resultSet.json()) as unknown as ClickhouseQueryResponse<{ - version?: string[]; - }>; + const rows = (await resultSet.json()) as unknown as ClickhouseQueryResponse<{ + version?: string[]; + }>; const versions = rows.data.version; if (!versions) { @@ -238,20 +237,20 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { if (index === -1) { if (!i.isZero()) { const last = i.sub(ONE); - await kv.put("ledger.latestVersion", last.toString()); + await kv.put('ledger.latestVersion', last.toString()); } return; } } - await kv.put("ledger.latestVersion", end.toString()); + await kv.put('ledger.latestVersion', end.toString()); ledgerLatestVersion = end.clone(); } } private async processVersion(version: string) { const res = await axios({ - method: "GET", + method: 'GET', url: `${this.providerHost}/v1/transactions?${qs.stringify({ start: version, limit: 1, @@ -268,13 +267,13 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { private async addVersionJob(version: bigint) { await this.olVersionQueue.add( - "version", + 'version', { version: version.toString(10) }, { jobId: `__version__${version}`, attempts: 3, backoff: { - type: "exponential", + type: 'exponential', delay: 500, }, removeOnComplete: { @@ -297,20 +296,15 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { } private async ingestTransaction(transaction: AptosTypes.Transaction) { - if (transaction.type === "pending_transaction") { + if (transaction.type === 'pending_transaction') { return; } - const dest = await fs.promises.mkdtemp( - pathUtil.join(os.tmpdir(), "ol-version-"), - ); + const dest = await fs.promises.mkdtemp(pathUtil.join(os.tmpdir(), 'ol-version-')); await fs.promises.mkdir(dest, { recursive: true }); const transactionsFile = `${dest}/transactions.json`; - await fs.promises.writeFile( - `${dest}/transactions.json`, - JSON.stringify([transaction]), - ); + await fs.promises.writeFile(`${dest}/transactions.json`, JSON.stringify([transaction])); const notPendingTransaction = transaction as NotPendingTransaction; @@ -331,14 +325,10 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { return; } - const parquetDest = await this.transformerService.transform([ - transactionsFile, - ]); + const parquetDest = await this.transformerService.transform([transactionsFile]); const files = await fs.promises.readdir(parquetDest); for (const file of files) { - await this.clickhouseService.insertParquetFile( - pathUtil.join(parquetDest, file), - ); + await this.clickhouseService.insertParquetFile(pathUtil.join(parquetDest, file)); } await fs.promises.rm(parquetDest, { recursive: true, force: true }); @@ -354,31 +344,27 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { }, }); - await this.walletSubscriptionService.releaseVersion( - notPendingTransaction.version, - ); + await this.walletSubscriptionService.releaseVersion(notPendingTransaction.version); await this.publishChanges(notPendingTransaction.version); - if (transaction.type === "user_transaction") { + if (transaction.type === 'user_transaction') { const userTransaction = transaction as AptosTypes.UserTransaction; const txHash = parseHexString(userTransaction.hash); const transactionUpdated = await this.transactionsService.updateTransactionStatus( - txHash, - undefined, - PendingTransactionStatus.ON_CHAIN, - ); + txHash, + undefined, + PendingTransactionStatus.ON_CHAIN, + ); // `updateTransactionStatus` returns false if no pending transaction was found. We need to explicitly trigger // the transaction event in that case. if (!transactionUpdated) { this.natsService.nc.publish( - this.natsService.getWalletTransactionChannel( - parseHexString(userTransaction.sender), - ), + this.natsService.getWalletTransactionChannel(parseHexString(userTransaction.sender)), OlVersionProcessor.jsonCodec.encode({ - hash: Buffer.from(txHash).toString("hex").toUpperCase(), + hash: Buffer.from(txHash).toString('hex').toUpperCase(), }), ); } @@ -404,7 +390,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { query_params: { version, }, - format: "JSONColumnsWithMetadata", + format: 'JSONColumnsWithMetadata', }); const rows = (await result.json()) as unknown as ClickhouseQueryResponse<{ @@ -416,7 +402,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { for (const address of rows.data.address) { this.natsService.nc.publish( - this.natsService.getWalletMovementChannel(Buffer.from(address, "hex")), + this.natsService.getWalletMovementChannel(Buffer.from(address, 'hex')), OlVersionProcessor.jsonCodec.encode({ version, }), @@ -426,7 +412,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { private async getLedgerVersion(): Promise { const res = await axios({ - method: "GET", + method: 'GET', url: `${this.providerHost}/v1`, signal: AbortSignal.timeout(5 * 60 * 1_000), // 5 minutes }); @@ -435,8 +421,7 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { } private async getMissingVersions() { - const lastBatchIngestedVersion = - await this.olDbService.getLastBatchIngestedVersion(); + const lastBatchIngestedVersion = await this.olDbService.getLastBatchIngestedVersion(); const latestStableVersion = await this.olService.getLatestStableVersion(); let fromVersion: BN | undefined; @@ -454,14 +439,10 @@ export class OlVersionProcessor extends WorkerHost implements OnModuleInit { } } - const ingestedVersions = await this.olDbService.getIngestedVersions( - fromVersion - ); + const ingestedVersions = await this.olDbService.getIngestedVersions(fromVersion); const latestVersion = new BN(await this.getLedgerVersion()); for ( - let i = fromVersion - ? fromVersion.add(ONE) - : ZERO; + let i = fromVersion ? fromVersion.add(ONE) : ZERO; i.lt(latestVersion); i = i.add(new BN(ONE)) ) { diff --git a/api/src/ol/ol.controller.ts b/api/src/ol/ol.controller.ts index 25f18cd..fb11f94 100644 --- a/api/src/ol/ol.controller.ts +++ b/api/src/ol/ol.controller.ts @@ -1,5 +1,5 @@ -import { Controller, Get, Param } from "@nestjs/common"; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; +import { Controller, Get, Param } from '@nestjs/common'; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; // Version 0's timestamp is calculated by dedubting the intervation between epoch 2 and 3 to epoch 2's timestamp. // @@ -17,8 +17,8 @@ const V0_TIMESTAMP = 1701203279; export class OlController { public constructor(private readonly clickhouseService: ClickhouseService) {} - @Get("/historical-balance/:address") - public async historicalBalance(@Param("address") address: string) { + @Get('/historical-balance/:address') + public async historicalBalance(@Param('address') address: string) { const query = ` SELECT tupleElement("entry", 2) / 1e6 AS "value", @@ -47,7 +47,7 @@ export class OlController { const resultSet = await this.clickhouseService.client.query({ query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const serie = await resultSet.json<{ @@ -58,7 +58,7 @@ export class OlController { for (let i = 0; i < serie.length; ++i) { const it = serie[i]; if (it.time === 0) { - it.time = V0_TIMESTAMP + it.time = V0_TIMESTAMP; } else { break; } diff --git a/api/src/ol/ol.module.ts b/api/src/ol/ol.module.ts index 3750cc3..d16848c 100644 --- a/api/src/ol/ol.module.ts +++ b/api/src/ol/ol.module.ts @@ -1,66 +1,66 @@ -import process from "node:process"; +import process from 'node:process'; -import { BullModule } from "@nestjs/bullmq"; -import { Module, Scope, Type } from "@nestjs/common"; +import { BullModule } from '@nestjs/bullmq'; +import { Module, Scope, Type } from '@nestjs/common'; -import { redisClient } from "../redis/redis.service.js"; -import { ClickhouseModule } from "../clickhouse/clickhouse.module.js"; -import { ModulesResolver } from "./modules.resolver.js"; -import { OlService } from "./ol.service.js"; -import { S3Module } from "../s3/s3.module.js"; -import { PrismaModule } from "../prisma/prisma.module.js"; -import { NatsModule } from "../nats/nats.module.js"; +import { redisClient } from '../redis/redis.service.js'; +import { ClickhouseModule } from '../clickhouse/clickhouse.module.js'; +import { ModulesResolver } from './modules.resolver.js'; +import { OlService } from './ol.service.js'; +import { S3Module } from '../s3/s3.module.js'; +import { PrismaModule } from '../prisma/prisma.module.js'; +import { NatsModule } from '../nats/nats.module.js'; -import { Types } from "../types.js"; +import { Types } from '../types.js'; -import { UserTransactionsResolver } from "./user-transactions.resolver.js"; +import { UserTransactionsResolver } from './user-transactions.resolver.js'; -import { OlVersionBatchProcessor } from "./ol-version-batch.processor.js"; -import { OlVersionProcessor } from "./ol-version.processor.js"; -import { OlDbModule } from "../ol-db/ol-db.module.js"; -import { OlParquetProducerProcessor } from "./ol-parquet-producer.processor.js"; -import { OlClickhouseIngestorProcessor } from "./ol-clickhouse-ingestor.processor.js"; -import { OlController } from "./ol.controller.js"; +import { OlVersionBatchProcessor } from './ol-version-batch.processor.js'; +import { OlVersionProcessor } from './ol-version.processor.js'; +import { OlDbModule } from '../ol-db/ol-db.module.js'; +import { OlParquetProducerProcessor } from './ol-parquet-producer.processor.js'; +import { OlClickhouseIngestorProcessor } from './ol-clickhouse-ingestor.processor.js'; +import { OlController } from './ol.controller.js'; -import { ValidatorsResolver } from "./validators/validators.resolver.js"; -import { ValidatorsProcessor } from "./validators/validators.processor.js"; -import { ValidatorsService } from "./validators/validators.service.js"; +import { ValidatorsResolver } from './validators/validators.resolver.js'; +import { ValidatorsProcessor } from './validators/validators.processor.js'; +import { ValidatorsService } from './validators/validators.service.js'; -import { AccountResolver } from "./account.resolver.js"; -import { AccountsResolver } from "./accounts/accounts.resolver.js"; -import { AccountsService } from "./accounts/accounts.service.js"; -import { AccountsProcessor } from "./accounts/accounts.processor.js"; +import { AccountResolver } from './account.resolver.js'; +import { AccountsResolver } from './accounts/accounts.resolver.js'; +import { AccountsService } from './accounts/accounts.service.js'; +import { AccountsProcessor } from './accounts/accounts.processor.js'; -import { TransformerService } from "./transformer.service.js"; +import { TransformerService } from './transformer.service.js'; -import { WalletSubscriptionModule } from "../wallet-subscription/wallet-subscription.module.js"; +import { WalletSubscriptionModule } from '../wallet-subscription/wallet-subscription.module.js'; -import { MovementsResolver } from "./movements/movements.resolver.js"; -import { MovementsService } from "./movements/movements.service.js"; +import { MovementsResolver } from './movements/movements.resolver.js'; +import { MovementsService } from './movements/movements.service.js'; -import { CommunityWalletsResolver } from "./community-wallets/community-wallets.resolver.js"; -import { CommunityWalletsService } from "./community-wallets/community-wallets.service.js"; -import { CommunityWalletsProcessor } from "./community-wallets/community-wallets.processor.js"; +import { CommunityWalletsResolver } from './community-wallets/community-wallets.resolver.js'; +import { CommunityWalletsService } from './community-wallets/community-wallets.service.js'; +import { CommunityWalletsProcessor } from './community-wallets/community-wallets.processor.js'; -import { TransactionsResolver } from "./transactions/TransactionsResolver.js"; -import { TransactionResolver } from "./transactions/TransactionResolver.js"; -import { TransactionsFactory } from "./transactions/TransactionsFactory.js"; -import { TransactionsRepository } from "./transactions/TransactionsRepository.js"; -import { TransactionsService } from "./transactions/TransactionsService.js"; -import { Transaction } from "./transactions/Transaction.js"; -import { OnChainTransactionsRepository } from "./transactions/OnChainTransactionsRepository.js"; -import { ExpiredTransactionsProcessor } from "./transactions/ExpiredTransactionsProcessor.js"; -import { InfoResolver } from "./info.resolver.js"; +import { TransactionsResolver } from './transactions/TransactionsResolver.js'; +import { TransactionResolver } from './transactions/TransactionResolver.js'; +import { TransactionsFactory } from './transactions/TransactionsFactory.js'; +import { TransactionsRepository } from './transactions/TransactionsRepository.js'; +import { TransactionsService } from './transactions/TransactionsService.js'; +import { Transaction } from './transactions/Transaction.js'; +import { OnChainTransactionsRepository } from './transactions/OnChainTransactionsRepository.js'; +import { ExpiredTransactionsProcessor } from './transactions/ExpiredTransactionsProcessor.js'; +import { InfoResolver } from './info.resolver.js'; -const roles = process.env.ROLES!.split(","); +const roles = process.env.ROLES!.split(','); const workersMap = new Map>([ - ["version-batch-processor", OlVersionBatchProcessor], - ["parquet-producer-processor", OlParquetProducerProcessor], - ["version-processor", OlVersionProcessor], - ["clickhouse-ingestor-processor", OlClickhouseIngestorProcessor], - ["expired-transactions-processor", ExpiredTransactionsProcessor], - ["accounts-processor", AccountsProcessor], + ['version-batch-processor', OlVersionBatchProcessor], + ['parquet-producer-processor', OlParquetProducerProcessor], + ['version-processor', OlVersionProcessor], + ['clickhouse-ingestor-processor', OlClickhouseIngestorProcessor], + ['expired-transactions-processor', ExpiredTransactionsProcessor], + ['accounts-processor', AccountsProcessor], ]); const workers: Type[] = []; @@ -82,42 +82,42 @@ for (const role of roles) { WalletSubscriptionModule, BullModule.registerQueue({ - name: "ol-clickhouse-ingestor", + name: 'ol-clickhouse-ingestor', connection: redisClient, }), BullModule.registerQueue({ - name: "ol-parquet-producer", + name: 'ol-parquet-producer', connection: redisClient, }), BullModule.registerQueue({ - name: "ol-version-batch", + name: 'ol-version-batch', connection: redisClient, }), BullModule.registerQueue({ - name: "ol-version", + name: 'ol-version', connection: redisClient, }), BullModule.registerQueue({ - name: "expired-transactions", + name: 'expired-transactions', connection: redisClient, }), BullModule.registerQueue({ - name: "accounts", + name: 'accounts', connection: redisClient, }), BullModule.registerQueue({ - name: "community-wallets", + name: 'community-wallets', connection: redisClient, }), BullModule.registerQueue({ - name: "validators", + name: 'validators', connection: redisClient, }), ], diff --git a/api/src/ol/ol.service.ts b/api/src/ol/ol.service.ts index a67ab4a..93238de 100644 --- a/api/src/ol/ol.service.ts +++ b/api/src/ol/ol.service.ts @@ -1,10 +1,10 @@ -import { ApiError, AptosClient } from "aptos"; -import { Injectable } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; -import BN from "bn.js"; -import { Decimal } from "decimal.js"; +import { ApiError, AptosClient } from 'aptos'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import BN from 'bn.js'; +import { Decimal } from 'decimal.js'; -import { OlConfig } from "../config/config.interface.js"; +import { OlConfig } from '../config/config.interface.js'; import { CoinStoreResource, ConsensusReward, @@ -14,12 +14,12 @@ import { ValidatorConfig, ValidatorGrade, ValidatorSet, -} from "./types.js"; -import { NetworkAddresses } from "./network-addresses.js"; -import { parseAddress } from "../utils.js"; -import { SupplyStats } from "./types.js"; -import { SlowWallet } from "./models/slow-wallet.model.js"; -import { NatsService } from "../nats/nats.service.js"; +} from './types.js'; +import { NetworkAddresses } from './network-addresses.js'; +import { parseAddress } from '../utils.js'; +import { SupplyStats } from './types.js'; +import { SlowWallet } from './models/slow-wallet.model.js'; +import { NatsService } from '../nats/nats.service.js'; @Injectable() export class OlService { @@ -27,15 +27,15 @@ export class OlService { public constructor( private readonly natsService: NatsService, - configService: ConfigService + configService: ConfigService, ) { - const config = configService.get("ol")!; + const config = configService.get('ol')!; this.aptosClient = new AptosClient(config.provider); } public async getSupplyStats(): Promise { const supplyStats = await this.aptosClient.view({ - function: "0x1::supply::get_stats", + function: '0x1::supply::get_stats', type_arguments: [], arguments: [], }); @@ -50,7 +50,7 @@ export class OlService { public async getCurrentValidators(): Promise { const addresses = await this.aptosClient.view({ - function: "0x1::stake::get_current_validators", + function: '0x1::stake::get_current_validators', type_arguments: [], arguments: [], }); @@ -59,16 +59,16 @@ export class OlService { public async accountExists(address: Buffer): Promise { const res = await this.aptosClient.view({ - function: "0x1::account::exists_at", + function: '0x1::account::exists_at', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); return res[0] as boolean; } public async getEligibleValidators(): Promise { const res = await this.aptosClient.view({ - function: "0x1::validator_universe::get_eligible_validators", + function: '0x1::validator_universe::get_eligible_validators', type_arguments: [], arguments: [], }); @@ -78,9 +78,9 @@ export class OlService { public async getCurrentBid(address: Buffer) { const res = await this.aptosClient.view({ - function: "0x1::proof_of_fee::current_bid", + function: '0x1::proof_of_fee::current_bid', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); const currentBid = res as [string, string]; return { @@ -92,9 +92,9 @@ export class OlService { public async getValidatorGrade(address: Buffer): Promise { try { const res = await this.aptosClient.view({ - function: "0x1::grade::get_validator_grade", + function: '0x1::grade::get_validator_grade', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); const payload = res as [boolean, string, string]; return { @@ -113,26 +113,26 @@ export class OlService { public async getAllVouchers(address: Buffer) { const res = await this.aptosClient.view({ - function: "0x1::vouch::all_vouchers", + function: '0x1::vouch::all_vouchers', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); return res; } public async getVouchersInValSet(address: Buffer) { const res = await this.aptosClient.view({ - function: "0x1::vouch::true_friends", + function: '0x1::vouch::true_friends', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); return res; } public async getConsensusReward(): Promise { const res = await this.aptosClient.getAccountResource( - "0x1", - "0x1::proof_of_fee::ConsensusReward", + '0x1', + '0x1::proof_of_fee::ConsensusReward', ); const consensusRewardRes = res.data as { clearing_bid: string; @@ -157,20 +157,17 @@ export class OlService { } public async getValidatorSet(): Promise { - const res = await this.aptosClient.getAccountResource( - "0x1", - "0x1::stake::ValidatorSet", - ); + const res = await this.aptosClient.getAccountResource('0x1', '0x1::stake::ValidatorSet'); const data = res.data as RawValidatorSet; return { activeValidators: data.active_validators.map((validator) => { const fullnodeAddresses = Buffer.from( validator.config.fullnode_addresses.substring(2), - "hex", + 'hex', ); const networkAddresses = Buffer.from( validator.config.network_addresses.substring(2), - "hex", + 'hex', ); return { @@ -178,10 +175,8 @@ export class OlService { votingPower: new BN(validator.voting_power), config: { consensusPubkey: validator.config.consensus_pubkey, - fullnodeAddresses: - NetworkAddresses.fromBytes(fullnodeAddresses)?.toString(), - networkAddresses: - NetworkAddresses.fromBytes(networkAddresses)?.toString(), + fullnodeAddresses: NetworkAddresses.fromBytes(fullnodeAddresses)?.toString(), + networkAddresses: NetworkAddresses.fromBytes(networkAddresses)?.toString(), validatorIndex: new BN(validator.config.validator_index), }, }; @@ -191,22 +186,18 @@ export class OlService { public async getValidatorConfig(address: Buffer): Promise { const res = await this.aptosClient.view({ - function: "0x1::stake::get_validator_config", + function: '0x1::stake::get_validator_config', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); const config = res as [string, string, string]; const fullnodeAddresses = config[1] - ? NetworkAddresses.fromBytes( - Buffer.from(config[1].substring(2), "hex"), - )?.toString() - : ""; + ? NetworkAddresses.fromBytes(Buffer.from(config[1].substring(2), 'hex'))?.toString() + : ''; const networkAddresses = config[2] - ? NetworkAddresses.fromBytes( - Buffer.from(config[2].substring(2), "hex"), - )?.toString() - : ""; + ? NetworkAddresses.fromBytes(Buffer.from(config[2].substring(2), 'hex'))?.toString() + : ''; return { consensus_pubkey: config[0], fullnode_addresses: fullnodeAddresses, @@ -215,10 +206,7 @@ export class OlService { } public async getCommunityWallets(): Promise { - const res = await this.aptosClient.getAccountResource( - "0x1", - "0x1::donor_voice::Registry", - ); + const res = await this.aptosClient.getAccountResource('0x1', '0x1::donor_voice::Registry'); const data = res.data as RawDonorVoiceRegistry; return data.list.map((it) => it.substring(2)); } @@ -226,16 +214,14 @@ export class OlService { public async getAccountBalance(address: Uint8Array): Promise { try { const res = await this.aptosClient.getAccountResource( - `0x${Buffer.from(address).toString("hex")}`, - "0x1::coin::CoinStore<0x1::libra_coin::LibraCoin>", + `0x${Buffer.from(address).toString('hex')}`, + '0x1::coin::CoinStore<0x1::libra_coin::LibraCoin>', ); - const balance = new Decimal( - (res.data as CoinStoreResource).coin.value, - ).div(1e6); + const balance = new Decimal((res.data as CoinStoreResource).coin.value).div(1e6); return balance; } catch (error) { if (error instanceof ApiError) { - if (error.errorCode === "resource_not_found") { + if (error.errorCode === 'resource_not_found') { return null; } } @@ -246,8 +232,8 @@ export class OlService { public async getSlowWallet(address: Uint8Array): Promise { try { const res = await this.aptosClient.getAccountResource( - `0x${Buffer.from(address).toString("hex")}`, - "0x1::slow_wallet::SlowWallet", + `0x${Buffer.from(address).toString('hex')}`, + '0x1::slow_wallet::SlowWallet', ); const slowWallet = res.data as SlowWalletResource; return new SlowWallet({ @@ -256,7 +242,7 @@ export class OlService { }); } catch (error) { if (error instanceof ApiError) { - if (error.errorCode === "resource_not_found") { + if (error.errorCode === 'resource_not_found') { return null; } } @@ -266,8 +252,8 @@ export class OlService { public async getLatestStableVersion(): Promise { const js = this.natsService.jetstream; - const kv = await js.views.kv("ol"); - const entry = await kv.get("ledger.latestVersion"); + const kv = await js.views.kv('ol'); + const entry = await kv.get('ledger.latestVersion'); if (!entry) { return null; } diff --git a/api/src/ol/transactions/ExpiredTransactionsProcessor.ts b/api/src/ol/transactions/ExpiredTransactionsProcessor.ts index 2515391..6113d94 100644 --- a/api/src/ol/transactions/ExpiredTransactionsProcessor.ts +++ b/api/src/ol/transactions/ExpiredTransactionsProcessor.ts @@ -1,28 +1,25 @@ -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import _ from "lodash"; -import { Inject, OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; -import { ConfigService } from "@nestjs/config"; -import axios from "axios"; -import BN from "bn.js"; -import { ApiError } from "aptos"; -import { PendingTransactionStatus } from "@prisma/client"; - -import { OlConfig } from "../../config/config.interface.js"; -import { Types } from "../../types.js"; -import { ITransactionsService } from "./interfaces.js"; -import { OlService } from "../ol.service.js"; -import { parseHexString } from "../../utils.js"; - -@Processor("expired-transactions") -export class ExpiredTransactionsProcessor - extends WorkerHost - implements OnModuleInit -{ +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import _ from 'lodash'; +import { Inject, OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; +import { ConfigService } from '@nestjs/config'; +import axios from 'axios'; +import BN from 'bn.js'; +import { ApiError } from 'aptos'; +import { PendingTransactionStatus } from '@prisma/client'; + +import { OlConfig } from '../../config/config.interface.js'; +import { Types } from '../../types.js'; +import { ITransactionsService } from './interfaces.js'; +import { OlService } from '../ol.service.js'; +import { parseHexString } from '../../utils.js'; + +@Processor('expired-transactions') +export class ExpiredTransactionsProcessor extends WorkerHost implements OnModuleInit { private readonly providerHost: string; public constructor( - @InjectQueue("expired-transactions") + @InjectQueue('expired-transactions') private readonly expiredTransactionsQueue: Queue, @Inject(Types.ITransactionsService) @@ -34,33 +31,27 @@ export class ExpiredTransactionsProcessor ) { super(); - const config = configService.get("ol")!; + const config = configService.get('ol')!; this.providerHost = config.provider; } public async onModuleInit() { - await this.expiredTransactionsQueue.add( - "findExpiredTransactions", - undefined, - { - repeat: { - every: 5 * 1_000, // 5 seconds - }, - removeOnComplete: true, + await this.expiredTransactionsQueue.add('findExpiredTransactions', undefined, { + repeat: { + every: 5 * 1_000, // 5 seconds }, - ); + removeOnComplete: true, + }); } public async process(job: Job) { switch (job.name) { - case "findExpiredTransactions": + case 'findExpiredTransactions': await this.findExpiredTransactions(); break; - case "expireTransaction": - await this.expireTransaction( - new Uint8Array(Buffer.from(job.data.hash, "hex")), - ); + case 'expireTransaction': + await this.expireTransaction(new Uint8Array(Buffer.from(job.data.hash, 'hex'))); break; default: @@ -70,7 +61,7 @@ export class ExpiredTransactionsProcessor private async getLedgerTimestamp(): Promise { const res = await axios({ - method: "GET", + method: 'GET', url: `${this.providerHost}/v1`, signal: AbortSignal.timeout(5 * 60 * 1_000), // 5 minutes }); @@ -83,13 +74,13 @@ export class ExpiredTransactionsProcessor try { const tx = await this.olService.aptosClient.getTransactionByHash( - `0x${transactionHashBuff.toString("hex")}`, + `0x${transactionHashBuff.toString('hex')}`, ); const txHash = parseHexString(tx.hash); if (!Buffer.from(txHash).equals(transactionHashBuff)) { throw new Error( - `transaction hash retrieved is different than the one provided provided=${transactionHashBuff.toString("hex")} returned=${Buffer.from(txHash).toString("hex")}`, + `transaction hash retrieved is different than the one provided provided=${transactionHashBuff.toString('hex')} returned=${Buffer.from(txHash).toString('hex')}`, ); } @@ -100,7 +91,7 @@ export class ExpiredTransactionsProcessor ); } catch (error) { if (error instanceof ApiError) { - if (error.errorCode === "transaction_not_found") { + if (error.errorCode === 'transaction_not_found') { await this.transactionsService.updateTransactionStatus( transactionHash, PendingTransactionStatus.UNKNOWN, @@ -118,20 +109,22 @@ export class ExpiredTransactionsProcessor const ledgerTimestamp = await this.getLedgerTimestamp(); const timestamp = ledgerTimestamp.div(new BN(1e6)).toNumber() + 1; - const transactionHashes = - await this.transactionsService.getTransactionsExpiredAfter(timestamp, 50); + const transactionHashes = await this.transactionsService.getTransactionsExpiredAfter( + timestamp, + 50, + ); for (const transactionHash of transactionHashes) { - const hash = Buffer.from(transactionHash).toString("hex"); + const hash = Buffer.from(transactionHash).toString('hex'); await this.expiredTransactionsQueue.add( - "expireTransaction", + 'expireTransaction', { hash }, { jobId: `__transaction__${hash}`, attempts: 10, backoff: { - type: "fixed", + type: 'fixed', }, removeOnComplete: { age: 3600, // keep up to 1 hour diff --git a/api/src/ol/transactions/OnChainTransactionsRepository.ts b/api/src/ol/transactions/OnChainTransactionsRepository.ts index 6e5b907..24fa512 100644 --- a/api/src/ol/transactions/OnChainTransactionsRepository.ts +++ b/api/src/ol/transactions/OnChainTransactionsRepository.ts @@ -1,5 +1,5 @@ -import { Injectable } from "@nestjs/common"; -import BN from "bn.js"; +import { Injectable } from '@nestjs/common'; +import BN from 'bn.js'; import { BlockMetadataTransactionDbEntity, @@ -7,18 +7,16 @@ import { IOnChainTransactionsRepository, ScriptUserTransactionDbEntity, UserTransactionDbEntity, -} from "./interfaces.js"; -import { ClickhouseService } from "../../clickhouse/clickhouse.service.js"; -import { UserTransaction } from "../models/UserTransaction.js"; -import { BlockMetadataTransaction } from "../models/BlockMetadataTransaction.js"; -import { ScriptUserTransaction } from "../models/ScriptUserTransaction.js"; -import { GenesisTransaction } from "../models/GenesisTransaction.js"; -import { AbstractTransaction } from "../models/Transaction.js"; +} from './interfaces.js'; +import { ClickhouseService } from '../../clickhouse/clickhouse.service.js'; +import { UserTransaction } from '../models/UserTransaction.js'; +import { BlockMetadataTransaction } from '../models/BlockMetadataTransaction.js'; +import { ScriptUserTransaction } from '../models/ScriptUserTransaction.js'; +import { GenesisTransaction } from '../models/GenesisTransaction.js'; +import { AbstractTransaction } from '../models/Transaction.js'; @Injectable() -export class OnChainTransactionsRepository - implements IOnChainTransactionsRepository -{ +export class OnChainTransactionsRepository implements IOnChainTransactionsRepository { public constructor(private readonly clickhouseService: ClickhouseService) {} public async getTransactionsByHashes( @@ -54,21 +52,20 @@ export class OnChainTransactionsRepository query_params: { versions, }, - format: "JSONEachRow", + format: 'JSONEachRow', }); - const userTransactionRows = - await resUserTransaction.json(); + const userTransactionRows = await resUserTransaction.json(); const userTransactions = new Map( userTransactionRows.map((userTransaction) => [ userTransaction.version, new UserTransaction({ - hash: Buffer.from(userTransaction.hash, "hex"), - sender: Buffer.from(userTransaction.sender, "hex"), + hash: Buffer.from(userTransaction.hash, 'hex'), + sender: Buffer.from(userTransaction.sender, 'hex'), timestamp: new BN(userTransaction.timestamp), version: new BN(userTransaction.version), success: userTransaction.success, - moduleAddress: Buffer.from(userTransaction.module_address, "hex"), + moduleAddress: Buffer.from(userTransaction.module_address, 'hex'), moduleName: userTransaction.module_name, functionName: userTransaction.function_name, arguments: userTransaction.arguments, @@ -86,9 +83,8 @@ export class OnChainTransactionsRepository return new Map(); } - const blockMetadataTransactionRes = - await this.clickhouseService.client.query({ - query: ` + const blockMetadataTransactionRes = await this.clickhouseService.client.query({ + query: ` SELECT "timestamp", "version", @@ -98,11 +94,11 @@ export class OnChainTransactionsRepository WHERE "version" IN {versions:Array(UInt64)} `, - query_params: { - versions, - }, - format: "JSONEachRow", - }); + query_params: { + versions, + }, + format: 'JSONEachRow', + }); const blockMetadataTransactionRows = await blockMetadataTransactionRes.json(); @@ -115,7 +111,7 @@ export class OnChainTransactionsRepository timestamp: new BN(blockMetadataTransaction.timestamp), version: new BN(blockMetadataTransaction.version), epoch: new BN(blockMetadataTransaction.epoch), - hash: Buffer.from(blockMetadataTransaction.hash, "hex"), + hash: Buffer.from(blockMetadataTransaction.hash, 'hex'), }), ]; }), @@ -146,7 +142,7 @@ export class OnChainTransactionsRepository query_params: { versions, }, - format: "JSONEachRow", + format: 'JSONEachRow', }); const scriptUserTransactionRows = @@ -157,8 +153,8 @@ export class OnChainTransactionsRepository scriptUserTransaction.version, new ScriptUserTransaction({ version: new BN(scriptUserTransaction.version), - hash: Buffer.from(scriptUserTransaction.hash, "hex"), - sender: Buffer.from(scriptUserTransaction.sender, "hex"), + hash: Buffer.from(scriptUserTransaction.hash, 'hex'), + sender: Buffer.from(scriptUserTransaction.sender, 'hex'), timestamp: new BN(scriptUserTransaction.timestamp), success: scriptUserTransaction.success, }), @@ -185,18 +181,17 @@ export class OnChainTransactionsRepository query_params: { versions, }, - format: "JSONEachRow", + format: 'JSONEachRow', }); - const genesisTransactionRows = - await genesisTransactionRes.json(); + const genesisTransactionRows = await genesisTransactionRes.json(); return new Map( genesisTransactionRows.map((genesisTransaction) => [ genesisTransaction.version, new GenesisTransaction({ version: new BN(genesisTransaction.version), - hash: Buffer.from(genesisTransaction.hash, "hex"), + hash: Buffer.from(genesisTransaction.hash, 'hex'), }), ]), ); @@ -239,24 +234,23 @@ export class OnChainTransactionsRepository "user_transaction"."hash" = "hashes"."hash" `, query_params: { - hashes: hashes.map((hash) => Buffer.from(hash).toString("hex")), + hashes: hashes.map((hash) => Buffer.from(hash).toString('hex')), }, - format: "JSONEachRow", + format: 'JSONEachRow', }); - const userTransactionRows = - await resUserTransaction.json(); + const userTransactionRows = await resUserTransaction.json(); const userTransactions = new Map( userTransactionRows.map((userTransaction) => [ - Buffer.from(userTransaction.hash, "hex").toString("hex").toUpperCase(), + Buffer.from(userTransaction.hash, 'hex').toString('hex').toUpperCase(), new UserTransaction({ - hash: Buffer.from(userTransaction.hash, "hex"), - sender: Buffer.from(userTransaction.sender, "hex"), + hash: Buffer.from(userTransaction.hash, 'hex'), + sender: Buffer.from(userTransaction.sender, 'hex'), timestamp: new BN(userTransaction.timestamp), version: new BN(userTransaction.version), success: userTransaction.success, - moduleAddress: Buffer.from(userTransaction.module_address, "hex"), + moduleAddress: Buffer.from(userTransaction.module_address, 'hex'), moduleName: userTransaction.module_name, functionName: userTransaction.function_name, arguments: userTransaction.arguments, @@ -313,7 +307,7 @@ export class OnChainTransactionsRepository SELECT MAX("timestamp") AS "timestamp" FROM "txs" `, - format: "JSONEachRow", + format: 'JSONEachRow', query_params: { version: version.toString(10), }, diff --git a/api/src/ol/transactions/Transaction.ts b/api/src/ol/transactions/Transaction.ts index eb46d6d..04f8cdf 100644 --- a/api/src/ol/transactions/Transaction.ts +++ b/api/src/ol/transactions/Transaction.ts @@ -1,13 +1,13 @@ -import { Field, ObjectType, registerEnumType } from "@nestjs/graphql"; -import { PendingTransactionStatus } from "@prisma/client"; +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; +import { PendingTransactionStatus } from '@prisma/client'; -import { ITransaction, TransactionArgs } from "./interfaces.js"; +import { ITransaction, TransactionArgs } from './interfaces.js'; registerEnumType(PendingTransactionStatus, { - name: "TransactionStatus", + name: 'TransactionStatus', }); -@ObjectType("Transaction") +@ObjectType('Transaction') export class Transaction implements ITransaction { @Field(() => Buffer) public hash: Uint8Array; diff --git a/api/src/ol/transactions/TransactionResolver.ts b/api/src/ol/transactions/TransactionResolver.ts index 345ebd1..aecdb3a 100644 --- a/api/src/ol/transactions/TransactionResolver.ts +++ b/api/src/ol/transactions/TransactionResolver.ts @@ -1,10 +1,10 @@ -import { Parent, ResolveField, Resolver } from "@nestjs/graphql"; -import { Inject } from "@nestjs/common"; +import { Parent, ResolveField, Resolver } from '@nestjs/graphql'; +import { Inject } from '@nestjs/common'; -import { Transaction } from "./Transaction.js"; -import { IOnChainTransactionsRepository, ITransaction } from "./interfaces.js"; -import { Types } from "../../types.js"; -import { AbstractTransaction } from "../models/AbstractTransaction.js"; +import { Transaction } from './Transaction.js'; +import { IOnChainTransactionsRepository, ITransaction } from './interfaces.js'; +import { Types } from '../../types.js'; +import { AbstractTransaction } from '../models/AbstractTransaction.js'; @Resolver(Transaction) export class TransactionResolver { @@ -17,10 +17,9 @@ export class TransactionResolver { public async onChainTransaction( @Parent() transaction: ITransaction, ): Promise { - const transactions = - await this.onChainTransactionsRepository.getTransactionsByHashes([ - transaction.hash, - ]); + const transactions = await this.onChainTransactionsRepository.getTransactionsByHashes([ + transaction.hash, + ]); const v = Array.from(transactions.values()); return v[0] ?? null; diff --git a/api/src/ol/transactions/TransactionsFactory.ts b/api/src/ol/transactions/TransactionsFactory.ts index d44fcb2..626a99c 100644 --- a/api/src/ol/transactions/TransactionsFactory.ts +++ b/api/src/ol/transactions/TransactionsFactory.ts @@ -1,11 +1,7 @@ -import { Inject, Injectable } from "@nestjs/common"; -import { ModuleRef } from "@nestjs/core"; -import { - ITransaction, - ITransactionsFactory, - TransactionArgs, -} from "./interfaces.js"; -import { Types } from "../../types.js"; +import { Inject, Injectable } from '@nestjs/common'; +import { ModuleRef } from '@nestjs/core'; +import { ITransaction, ITransactionsFactory, TransactionArgs } from './interfaces.js'; +import { Types } from '../../types.js'; @Injectable() export class TransactionsFactory implements ITransactionsFactory { @@ -13,9 +9,7 @@ export class TransactionsFactory implements ITransactionsFactory { private readonly moduleRef: ModuleRef; public async createTransaction(args: TransactionArgs): Promise { - const transaction = await this.moduleRef.resolve( - Types.ITransaction, - ); + const transaction = await this.moduleRef.resolve(Types.ITransaction); transaction.init(args); return transaction; } diff --git a/api/src/ol/transactions/TransactionsRepository.ts b/api/src/ol/transactions/TransactionsRepository.ts index 2bcaadc..6664d98 100644 --- a/api/src/ol/transactions/TransactionsRepository.ts +++ b/api/src/ol/transactions/TransactionsRepository.ts @@ -1,10 +1,10 @@ -import { Inject, Injectable } from "@nestjs/common"; -import { PendingTransactionStatus, Prisma } from "@prisma/client"; +import { Inject, Injectable } from '@nestjs/common'; +import { PendingTransactionStatus, Prisma } from '@prisma/client'; import { SignedTransaction, TransactionPayloadEntryFunction, TransactionAuthenticatorEd25519, -} from "@aptos-labs/ts-sdk"; +} from '@aptos-labs/ts-sdk'; import { IOnChainTransactionsRepository, @@ -12,11 +12,11 @@ import { ITransactionsFactory, ITransactionsRepository, TransactionArgs, -} from "./interfaces.js"; -import { PrismaService } from "../../prisma/prisma.service.js"; -import { Types } from "../../types.js"; -import { getTransactionHash } from "../../utils.js"; -import { UserTransaction } from "../models/UserTransaction.js"; +} from './interfaces.js'; +import { PrismaService } from '../../prisma/prisma.service.js'; +import { Types } from '../../types.js'; +import { getTransactionHash } from '../../utils.js'; +import { UserTransaction } from '../models/UserTransaction.js'; @Injectable() export class TransactionsRepository implements ITransactionsRepository { @@ -30,26 +30,18 @@ export class TransactionsRepository implements ITransactionsRepository { private readonly onChainTransactionsRepository: IOnChainTransactionsRepository, ) {} - public async newTransaction( - signedTransaction: SignedTransaction, - ): Promise { - if ( - signedTransaction.raw_txn.payload instanceof - TransactionPayloadEntryFunction - ) { + public async newTransaction(signedTransaction: SignedTransaction): Promise { + if (signedTransaction.raw_txn.payload instanceof TransactionPayloadEntryFunction) { const txHash = getTransactionHash(signedTransaction); - if ( - signedTransaction.authenticator instanceof - TransactionAuthenticatorEd25519 - ) { + if (signedTransaction.authenticator instanceof TransactionAuthenticatorEd25519) { const entryFunctionPayload = signedTransaction.raw_txn.payload; const { entryFunction } = entryFunctionPayload; entryFunction.function_name.identifier; const toBytea = (input: Uint8Array) => { - return `\\x${Buffer.from(input).toString("hex")}`; + return `\\x${Buffer.from(input).toString('hex')}`; }; const affectedRows = await this.prisma.$queryRaw<{ hash: Buffer }[]>` @@ -73,9 +65,7 @@ export class TransactionsRepository implements ITransactionsRepository { ${entryFunction.module_name.address.data}, ${entryFunction.module_name.name.identifier}, ${Prisma.raw( - `'{${entryFunction.args - .map((arg) => toBytea(arg.bcsToBytes())) - .join(",")}}'`, + `'{${entryFunction.args.map((arg) => toBytea(arg.bcsToBytes())).join(',')}}'`, )}, ${[]} ) @@ -87,16 +77,14 @@ export class TransactionsRepository implements ITransactionsRepository { } return false; } else { - throw new Error("unsupported transaction authenticator"); + throw new Error('unsupported transaction authenticator'); } } - throw new Error("unsupported transaction payload type"); + throw new Error('unsupported transaction payload type'); } - public async getWalletTransactions( - address: Uint8Array, - ): Promise { + public async getWalletTransactions(address: Uint8Array): Promise { const rows = await this.prisma.pendingTransaction.findMany({ where: { sender: Buffer.from(address), @@ -129,12 +117,11 @@ export class TransactionsRepository implements ITransactionsRepository { status: transaction.status, }; } else { - const onChainTransactions = - await this.onChainTransactionsRepository.getTransactionsByHashes([ - hash, - ]); + const onChainTransactions = await this.onChainTransactionsRepository.getTransactionsByHashes([ + hash, + ]); const onChainTransaction = onChainTransactions.get( - Buffer.from(hash).toString("hex").toUpperCase(), + Buffer.from(hash).toString('hex').toUpperCase(), ); if (onChainTransaction) { if (onChainTransaction instanceof UserTransaction) { @@ -148,7 +135,7 @@ export class TransactionsRepository implements ITransactionsRepository { } if (!args) { - throw new Error("transaction not found"); + throw new Error('transaction not found'); } return this.transactionsFactory.createTransaction(args); @@ -168,7 +155,7 @@ export class TransactionsRepository implements ITransactionsRepository { }, take: limit, orderBy: { - expirationTimestampSecs: "asc", + expirationTimestampSecs: 'asc', }, }); return transactions.map((tx) => tx.hash); @@ -184,7 +171,7 @@ export class TransactionsRepository implements ITransactionsRepository { UPDATE "PendingTransaction" SET "status" = (${to})::"PendingTransactionStatus" WHERE - "hash" = ${Prisma.raw(`'\\x${Buffer.from(hash).toString("hex")}'`)} + "hash" = ${Prisma.raw(`'\\x${Buffer.from(hash).toString('hex')}'`)} AND "status" = (${from})::"PendingTransactionStatus" RETURNING "hash" @@ -196,7 +183,7 @@ export class TransactionsRepository implements ITransactionsRepository { UPDATE "PendingTransaction" SET "status" = (${to})::"PendingTransactionStatus" WHERE - "hash" = ${Prisma.raw(`'\\x${Buffer.from(hash).toString("hex")}'`)} + "hash" = ${Prisma.raw(`'\\x${Buffer.from(hash).toString('hex')}'`)} AND "status" != (${to})::"PendingTransactionStatus" RETURNING "hash" diff --git a/api/src/ol/transactions/TransactionsResolver.ts b/api/src/ol/transactions/TransactionsResolver.ts index 1bf5bd6..779bbfc 100644 --- a/api/src/ol/transactions/TransactionsResolver.ts +++ b/api/src/ol/transactions/TransactionsResolver.ts @@ -1,16 +1,16 @@ -import { Args, Mutation, Resolver, Query, Subscription } from "@nestjs/graphql"; -import { Inject } from "@nestjs/common"; -import { Repeater } from "@repeaterjs/repeater"; -import { ConfigService } from "@nestjs/config"; - -import { Types } from "../../types.js"; -import { ITransaction, ITransactionsService } from "./interfaces.js"; -import { Transaction } from "./Transaction.js"; -import { NatsService } from "../../nats/nats.service.js"; -import { deserializeSignedTransaction, getTransactionHash, parseHexString } from "../../utils.js"; -import { OlConfig } from "../../config/config.interface.js"; -import axios from "axios"; -import { GraphQLError } from "graphql"; +import { Args, Mutation, Resolver, Query, Subscription } from '@nestjs/graphql'; +import { Inject } from '@nestjs/common'; +import { Repeater } from '@repeaterjs/repeater'; +import { ConfigService } from '@nestjs/config'; + +import { Types } from '../../types.js'; +import { ITransaction, ITransactionsService } from './interfaces.js'; +import { Transaction } from './Transaction.js'; +import { NatsService } from '../../nats/nats.service.js'; +import { deserializeSignedTransaction, getTransactionHash, parseHexString } from '../../utils.js'; +import { OlConfig } from '../../config/config.interface.js'; +import axios from 'axios'; +import { GraphQLError } from 'graphql'; @Resolver() export class TransactionsResolver { @@ -24,21 +24,21 @@ export class TransactionsResolver { configService: ConfigService, ) { - const config = configService.get("ol")!; + const config = configService.get('ol')!; this.providerHost = config.provider; } - @Query(() => [Transaction], { name: "walletTransactions" }) + @Query(() => [Transaction], { name: 'walletTransactions' }) public async getWalletTransactions( - @Args("address", { type: () => Buffer }) + @Args('address', { type: () => Buffer }) address: Uint8Array, ): Promise { return this.transactionsService.getWalletTransactions(address); } - @Query(() => Transaction, { name: "transaction" }) + @Query(() => Transaction, { name: 'transaction' }) public async getTransaction( - @Args("hash", { type: () => Buffer }) + @Args('hash', { type: () => Buffer }) hash: Uint8Array, ): Promise { return this.transactionsService.getTransactionByHash(hash); @@ -46,7 +46,7 @@ export class TransactionsResolver { @Mutation(() => Transaction) public async newTransaction( - @Args("signedTransaction", { type: () => Buffer }) + @Args('signedTransaction', { type: () => Buffer }) signedTransaction: Buffer, ) { const tx = deserializeSignedTransaction(signedTransaction); @@ -54,17 +54,20 @@ export class TransactionsResolver { const txHash = Buffer.from(getTransactionHash(tx)); - const res = await axios<{ - hash: string; - } | { - error_code: string; - message: string; - vm_error_code: number; - }>({ - method: "POST", + const res = await axios< + | { + hash: string; + } + | { + error_code: string; + message: string; + vm_error_code: number; + } + >({ + method: 'POST', url: `${this.providerHost}/v1/transactions`, headers: { - "content-type": "application/x.diem.signed_transaction+bcs", + 'content-type': 'application/x.diem.signed_transaction+bcs', }, data: signedTransaction, signal: AbortSignal.timeout(1 * 60 * 1_000), // 1 minutes @@ -77,11 +80,11 @@ export class TransactionsResolver { const resHash = Buffer.from(parseHexString(body.hash)); if (!txHash.equals(resHash)) { throw new GraphQLError( - `transaction hash retrieved is different than the one provided provided=${txHash.toString("hex")} returned=${Buffer.from(resHash).toString("hex")}`, + `transaction hash retrieved is different than the one provided provided=${txHash.toString('hex')} returned=${Buffer.from(resHash).toString('hex')}`, ); } return this.transactionsService.getTransactionByHash(txHash); - }; + } case 400: { const body = res.data as { @@ -89,21 +92,17 @@ export class TransactionsResolver { message: string; vm_error_code: number; }; - throw new GraphQLError( - body.message, - ); - }; + throw new GraphQLError(body.message); + } default: - throw new GraphQLError( - `Error from rpc node. status = ${res.status}` - ); + throw new GraphQLError(`Error from rpc node. status = ${res.status}`); } } @Subscription((returns) => Transaction) public async walletTransaction( - @Args({ name: "address", type: () => Buffer }) + @Args({ name: 'address', type: () => Buffer }) address: Buffer, ) { return new Repeater(async (push, stop) => { @@ -115,10 +114,9 @@ export class TransactionsResolver { stop(err); } else { const { hash } = msg.json<{ hash: string }>(); - const transaction = - await this.transactionsService.getTransactionByHash( - Buffer.from(hash, "hex"), - ); + const transaction = await this.transactionsService.getTransactionByHash( + Buffer.from(hash, 'hex'), + ); push({ walletTransaction: transaction, diff --git a/api/src/ol/transactions/TransactionsService.ts b/api/src/ol/transactions/TransactionsService.ts index 2216336..49ec8e5 100644 --- a/api/src/ol/transactions/TransactionsService.ts +++ b/api/src/ol/transactions/TransactionsService.ts @@ -1,16 +1,12 @@ -import { JSONCodec } from "nats"; -import { Inject, Injectable } from "@nestjs/common"; -import { SignedTransaction } from "@aptos-labs/ts-sdk"; -import { PendingTransactionStatus } from "@prisma/client"; +import { JSONCodec } from 'nats'; +import { Inject, Injectable } from '@nestjs/common'; +import { SignedTransaction } from '@aptos-labs/ts-sdk'; +import { PendingTransactionStatus } from '@prisma/client'; -import { - ITransaction, - ITransactionsRepository, - ITransactionsService, -} from "./interfaces.js"; -import { Types } from "../../types.js"; -import { NatsService } from "../../nats/nats.service.js"; -import { getTransactionHash } from "../../utils.js"; +import { ITransaction, ITransactionsRepository, ITransactionsService } from './interfaces.js'; +import { Types } from '../../types.js'; +import { NatsService } from '../../nats/nats.service.js'; +import { getTransactionHash } from '../../utils.js'; @Injectable() export class TransactionsService implements ITransactionsService { @@ -23,9 +19,7 @@ export class TransactionsService implements ITransactionsService { private readonly transactionsRepository: ITransactionsRepository, ) {} - public async newTransaction( - signedTransaction: SignedTransaction, - ): Promise { + public async newTransaction(signedTransaction: SignedTransaction): Promise { if (await this.transactionsRepository.newTransaction(signedTransaction)) { const hash = getTransactionHash(signedTransaction); @@ -34,7 +28,7 @@ export class TransactionsService implements ITransactionsService { signedTransaction.raw_txn.sender.toUint8Array(), ), TransactionsService.jsonCodec.encode({ - hash: Buffer.from(hash).toString("hex").toUpperCase(), + hash: Buffer.from(hash).toString('hex').toUpperCase(), }), ); @@ -43,9 +37,7 @@ export class TransactionsService implements ITransactionsService { return false; } - public async getWalletTransactions( - address: Uint8Array, - ): Promise { + public async getWalletTransactions(address: Uint8Array): Promise { return this.transactionsRepository.getWalletTransactions(address); } @@ -57,10 +49,7 @@ export class TransactionsService implements ITransactionsService { timestamp: number, limit: number, ): Promise { - return this.transactionsRepository.getTransactionsExpiredAfter( - timestamp, - limit, - ); + return this.transactionsRepository.getTransactionsExpiredAfter(timestamp, limit); } public async updateTransactionStatus( @@ -68,14 +57,12 @@ export class TransactionsService implements ITransactionsService { from: PendingTransactionStatus | undefined, to: PendingTransactionStatus, ): Promise { - if ( - await this.transactionsRepository.updateTransactionStatus(hash, from, to) - ) { + if (await this.transactionsRepository.updateTransactionStatus(hash, from, to)) { const transaction = await this.getTransactionByHash(hash); this.natsService.nc.publish( this.natsService.getWalletTransactionChannel(transaction.sender), TransactionsService.jsonCodec.encode({ - hash: Buffer.from(transaction.hash).toString("hex").toUpperCase(), + hash: Buffer.from(transaction.hash).toString('hex').toUpperCase(), }), ); return true; diff --git a/api/src/ol/transactions/interfaces.ts b/api/src/ol/transactions/interfaces.ts index 84231e5..bedcd15 100644 --- a/api/src/ol/transactions/interfaces.ts +++ b/api/src/ol/transactions/interfaces.ts @@ -1,21 +1,18 @@ -import BN from "bn.js"; -import { PendingTransactionStatus } from "@prisma/client"; -import { SignedTransaction } from "@aptos-labs/ts-sdk"; +import BN from 'bn.js'; +import { PendingTransactionStatus } from '@prisma/client'; +import { SignedTransaction } from '@aptos-labs/ts-sdk'; -import { UserTransaction } from "../models/UserTransaction.js"; -import { BlockMetadataTransaction } from "../models/BlockMetadataTransaction.js"; -import { ScriptUserTransaction } from "../models/ScriptUserTransaction.js"; -import { GenesisTransaction } from "../models/GenesisTransaction.js"; -import { AbstractTransaction } from "../models/Transaction.js"; +import { UserTransaction } from '../models/UserTransaction.js'; +import { BlockMetadataTransaction } from '../models/BlockMetadataTransaction.js'; +import { ScriptUserTransaction } from '../models/ScriptUserTransaction.js'; +import { GenesisTransaction } from '../models/GenesisTransaction.js'; +import { AbstractTransaction } from '../models/Transaction.js'; export interface ITransactionsRepository { newTransaction(signedTransaction: SignedTransaction): Promise; getWalletTransactions(address: Uint8Array): Promise; getTransactionByHash(hash: Uint8Array): Promise; - getTransactionsExpiredAfter( - timestamp: number, - limit: number, - ): Promise; + getTransactionsExpiredAfter(timestamp: number, limit: number): Promise; updateTransactionStatus( hash: Uint8Array, from: PendingTransactionStatus | undefined, @@ -27,10 +24,7 @@ export interface ITransactionsService { newTransaction(signedTransaction: SignedTransaction): Promise; getWalletTransactions(address: Uint8Array): Promise; getTransactionByHash(hash: Uint8Array): Promise; - getTransactionsExpiredAfter( - timestamp: number, - limit: number, - ): Promise; + getTransactionsExpiredAfter(timestamp: number, limit: number): Promise; updateTransactionStatus( hash: Uint8Array, from: PendingTransactionStatus | undefined, @@ -56,13 +50,9 @@ export interface ITransactionsFactory { } export interface IOnChainTransactionsRepository { - getTransactionsByHashes( - hashes: Uint8Array[], - ): Promise>; + getTransactionsByHashes(hashes: Uint8Array[]): Promise>; - getUserTransactionsByVersions( - versions: number[], - ): Promise>; + getUserTransactionsByVersions(versions: number[]): Promise>; getBlockMetadataTransactionsByVersions( versions: number[], @@ -72,9 +62,7 @@ export interface IOnChainTransactionsRepository { versions: number[], ): Promise>; - getGenesisTransactionsByVersions( - versions: number[], - ): Promise>; + getGenesisTransactionsByVersions(versions: number[]): Promise>; getTransactionTimestamp(version: BN): Promise; } diff --git a/api/src/ol/transformer.service.ts b/api/src/ol/transformer.service.ts index 1ca91c8..244c8ea 100644 --- a/api/src/ol/transformer.service.ts +++ b/api/src/ol/transformer.service.ts @@ -1,35 +1,30 @@ -import os from "node:os"; -import pathUtil from "node:path"; -import fs from "node:fs"; -import { spawn } from "node:child_process"; +import os from 'node:os'; +import pathUtil from 'node:path'; +import fs from 'node:fs'; +import { spawn } from 'node:child_process'; import process from 'node:process'; -import { Injectable } from "@nestjs/common"; +import { Injectable } from '@nestjs/common'; @Injectable() export class TransformerService { public async transform(txFiles: string[]): Promise { - const dest = await fs.promises.mkdtemp( - pathUtil.join(os.tmpdir(), "transfromer-"), - ); + const dest = await fs.promises.mkdtemp(pathUtil.join(os.tmpdir(), 'transfromer-')); await new Promise((resolve, reject) => { const bin = - process.env.NODE_ENV === "production" - ? "/usr/local/bin/transformer" - : pathUtil.join( - process.cwd(), - "transformer/target/debug/transformer", - ); + process.env.NODE_ENV === 'production' + ? '/usr/local/bin/transformer' + : pathUtil.join(process.cwd(), 'transformer/target/debug/transformer'); const proc = spawn(bin, [...txFiles, dest], { - stdio: "inherit", + stdio: 'inherit', }); // proc.stderr.pipe(process.stderr, { end: false }); // proc.stdout.pipe(process.stdout, { end: false }); - proc.on("close", (code) => { + proc.on('close', (code) => { if (code === 0) { resolve(); } else { diff --git a/api/src/ol/types.ts b/api/src/ol/types.ts index 3f09708..9a60a5c 100644 --- a/api/src/ol/types.ts +++ b/api/src/ol/types.ts @@ -1,5 +1,5 @@ -import { Types } from "aptos"; -import BN from "bn.js"; +import { Types } from 'aptos'; +import BN from 'bn.js'; export type NotPendingTransaction = | Types.UserTransaction diff --git a/api/src/ol/user-transactions.resolver.ts b/api/src/ol/user-transactions.resolver.ts index 5c85ced..30a171c 100644 --- a/api/src/ol/user-transactions.resolver.ts +++ b/api/src/ol/user-transactions.resolver.ts @@ -1,10 +1,10 @@ -import { Args, Int, Query, Resolver } from "@nestjs/graphql"; +import { Args, Int, Query, Resolver } from '@nestjs/graphql'; import { GqlUserTransactionDeprecated, GqlUserTransactionCollection, -} from "./models/transaction.model.js"; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; +} from './models/transaction.model.js'; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; @Resolver() export class UserTransactionsResolver { @@ -15,36 +15,36 @@ export class UserTransactionsResolver { const result = await this.clickhouseService.client .query({ query: 'SELECT COUNT(*) as "total" FROM user_transaction', - format: "JSONEachRow", + format: 'JSONEachRow', }) .then((res) => res.json<{ total: string }[]>()); // Verifica se o resultado está definido e se contém o campo 'total' if (result && result.length > 0) { - const total = Number(result[0]["total"]); + const total = Number(result[0]['total']); return total; } // Caso não haja resultado, retorna 0 ou lança um erro apropriado - throw new Error("Failed to fetch user transactions count"); + throw new Error('Failed to fetch user transactions count'); } @Query(() => GqlUserTransactionCollection) async userTransactions( - @Args({ name: "limit", type: () => Int }) + @Args({ name: 'limit', type: () => Int }) limit: number, - @Args({ name: "offset", type: () => Int }) + @Args({ name: 'offset', type: () => Int }) offset: number, - @Args({ name: "order", type: () => String }) + @Args({ name: 'order', type: () => String }) order: string, ): Promise { const [total, items] = await Promise.all([ this.clickhouseService.client .query({ query: 'SELECT COUNT(*) as "total" FROM user_transaction', - format: "JSONEachRow", + format: 'JSONEachRow', }) .then((res) => res.json<{ total: string }>()) .then((rows) => parseInt(rows[0].total, 10)), @@ -71,10 +71,10 @@ export class UserTransactionsResolver { "function_name", "timestamp" FROM "user_transaction" - ORDER BY "version" ${order === "ASC" ? "ASC" : "DESC"} + ORDER BY "version" ${order === 'ASC' ? 'ASC' : 'DESC'} LIMIT {limit:Int32} OFFSET {offset:Int32} `, - format: "JSONEachRow", + format: 'JSONEachRow', query_params: { limit, offset, diff --git a/api/src/ol/validators/validators.processor.ts b/api/src/ol/validators/validators.processor.ts index 6b799a1..b2305f3 100644 --- a/api/src/ol/validators/validators.processor.ts +++ b/api/src/ol/validators/validators.processor.ts @@ -1,15 +1,15 @@ // src/validators/validators.processor.ts -import { Processor, WorkerHost } from "@nestjs/bullmq"; -import { InjectQueue } from "@nestjs/bullmq"; -import { Queue, Job } from "bullmq"; -import { redisClient } from "../../redis/redis.service.js"; -import { ValidatorsService } from "./validators.service.js"; -import { VALIDATORS_CACHE_KEY } from "../constants.js"; +import { Processor, WorkerHost } from '@nestjs/bullmq'; +import { InjectQueue } from '@nestjs/bullmq'; +import { Queue, Job } from 'bullmq'; +import { redisClient } from '../../redis/redis.service.js'; +import { ValidatorsService } from './validators.service.js'; +import { VALIDATORS_CACHE_KEY } from '../constants.js'; -@Processor("validators") +@Processor('validators') export class ValidatorsProcessor extends WorkerHost { public constructor( - @InjectQueue("validators") + @InjectQueue('validators') private readonly validatorsQueue: Queue, private readonly validatorsService: ValidatorsService, ) { @@ -17,7 +17,7 @@ export class ValidatorsProcessor extends WorkerHost { } public async onModuleInit() { - await this.validatorsQueue.add("updateValidatorsCache", undefined, { + await this.validatorsQueue.add('updateValidatorsCache', undefined, { repeat: { every: 30 * 1000, // 30 seconds }, @@ -26,7 +26,7 @@ export class ValidatorsProcessor extends WorkerHost { public async process(job: Job) { switch (job.name) { - case "updateValidatorsCache": + case 'updateValidatorsCache': await this.updateValidatorsCache(); break; diff --git a/api/src/ol/validators/validators.resolver.ts b/api/src/ol/validators/validators.resolver.ts index 4256524..1bd9982 100644 --- a/api/src/ol/validators/validators.resolver.ts +++ b/api/src/ol/validators/validators.resolver.ts @@ -1,10 +1,10 @@ -import { ConfigService } from "@nestjs/config"; -import { Query, Resolver } from "@nestjs/graphql"; +import { ConfigService } from '@nestjs/config'; +import { Query, Resolver } from '@nestjs/graphql'; -import { ValidatorsService } from "./validators.service.js"; -import { Validator } from "../models/validator.model.js"; -import { redisClient } from "../../redis/redis.service.js"; -import { VALIDATORS_CACHE_KEY } from "../constants.js"; +import { ValidatorsService } from './validators.service.js'; +import { Validator } from '../models/validator.model.js'; +import { redisClient } from '../../redis/redis.service.js'; +import { VALIDATORS_CACHE_KEY } from '../constants.js'; @Resolver(() => Validator) export class ValidatorsResolver { @@ -14,7 +14,7 @@ export class ValidatorsResolver { private readonly validatorsService: ValidatorsService, config: ConfigService, ) { - this.cacheEnabled = config.get("cacheEnabled")!; + this.cacheEnabled = config.get('cacheEnabled')!; } @Query(() => [Validator]) diff --git a/api/src/ol/validators/validators.service.ts b/api/src/ol/validators/validators.service.ts index 6d7511d..4e336ed 100644 --- a/api/src/ol/validators/validators.service.ts +++ b/api/src/ol/validators/validators.service.ts @@ -1,11 +1,11 @@ -import { Injectable } from "@nestjs/common"; -import Bluebird from "bluebird"; -import BN from "bn.js"; +import { Injectable } from '@nestjs/common'; +import Bluebird from 'bluebird'; +import BN from 'bn.js'; -import { OlService } from "../ol.service.js"; -import { PrismaService } from "../../prisma/prisma.service.js"; -import { Validator, GqlVouch } from "../models/validator.model.js"; -import { parseAddress } from "../../utils.js"; +import { OlService } from '../ol.service.js'; +import { PrismaService } from '../../prisma/prisma.service.js'; +import { Validator, GqlVouch } from '../models/validator.model.js'; +import { parseAddress } from '../../utils.js'; @Injectable() export class ValidatorsService { @@ -28,11 +28,10 @@ export class ValidatorsService { validatorSet.activeValidators, async (validator) => { const valIp = - validator.config.networkAddresses && - validator.config.networkAddresses.split("/")[2]; - const node = nodes.find((node) => node["ip"] == valIp); - const city = node && node["city"] ? node["city"] : null; - const country = node && node["country"] ? node["country"] : null; + validator.config.networkAddresses && validator.config.networkAddresses.split('/')[2]; + const node = nodes.find((node) => node['ip'] == valIp); + const city = node && node['city'] ? node['city'] : null; + const country = node && node['country'] ? node['country'] : null; const grade = await this.olService.getValidatorGrade(validator.addr); return { @@ -52,8 +51,7 @@ export class ValidatorsService { const eligible = await this.olService.getEligibleValidators(); const eligibleCurrent = eligible.filter( - (address) => - !currentValidators.find((it) => !it.address.compare(address)), + (address) => !currentValidators.find((it) => !it.address.compare(address)), ); const eligibleValidators = await Promise.all( @@ -74,22 +72,16 @@ export class ValidatorsService { let allValidators = [...currentValidators, ...eligibleValidators]; return await Promise.all( allValidators.map(async (validator) => { - const balance = await this.olService.getAccountBalance( - validator.address, - ); - const slowWallet = await this.olService.getSlowWallet( - validator.address, - ); + const balance = await this.olService.getAccountBalance(validator.address); + const slowWallet = await this.olService.getSlowWallet(validator.address); const unlocked = Number(slowWallet?.unlocked); const vouches = await this.getVouches(validator.address); - const currentBid = await this.olService.getCurrentBid( - validator.address, - ); + const currentBid = await this.olService.getCurrentBid(validator.address); return new Validator({ inSet: validator.inSet, index: validator.index, - address: validator.address.toString("hex").toLocaleUpperCase(), + address: validator.address.toString('hex').toLocaleUpperCase(), votingPower: validator.votingPower, balance: Number(balance), unlocked: unlocked, @@ -105,23 +97,23 @@ export class ValidatorsService { } public async getAuditQualification(address: Buffer): Promise<[string]> { const auditQualification = await this.olService.aptosClient.view({ - function: "0x1::proof_of_fee::audit_qualification", + function: '0x1::proof_of_fee::audit_qualification', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); return auditQualification[0] as [string]; } public async getVouches(address: Buffer): Promise { const allVouchesRes = await this.olService.aptosClient.getAccountResource( - `0x${address.toString("hex")}`, - "0x1::vouch::MyVouches", + `0x${address.toString('hex')}`, + '0x1::vouch::MyVouches', ); const validVouchesRes = await this.olService.aptosClient.view({ - function: "0x1::vouch::true_friends", + function: '0x1::vouch::true_friends', type_arguments: [], - arguments: [`0x${address.toString("hex")}`], + arguments: [`0x${address.toString('hex')}`], }); const allVouches = allVouchesRes.data as { @@ -131,24 +123,20 @@ export class ValidatorsService { const all = allVouches.my_buddies.map((address, index) => { return { - address: parseAddress(address).toString("hex").toLocaleUpperCase(), + address: parseAddress(address).toString('hex').toLocaleUpperCase(), epoch: Number(allVouches.epoch_vouched[index]), }; }); let validVouches = validVouchesRes[0] as string[]; validVouches = validVouches.map((address) => - parseAddress(address).toString("hex").toLocaleUpperCase(), - ); - const activeVouches = all.filter((vouch) => - validVouches.includes(vouch.address), + parseAddress(address).toString('hex').toLocaleUpperCase(), ); + const activeVouches = all.filter((vouch) => validVouches.includes(vouch.address)); return activeVouches.map((vouch) => { return new GqlVouch({ - address: parseAddress(vouch.address) - .toString("hex") - .toLocaleUpperCase(), + address: parseAddress(vouch.address).toString('hex').toLocaleUpperCase(), epoch: Number(vouch.epoch), }); }); diff --git a/api/src/prisma/prisma.module.ts b/api/src/prisma/prisma.module.ts index fe0d83b..00b08f8 100644 --- a/api/src/prisma/prisma.module.ts +++ b/api/src/prisma/prisma.module.ts @@ -1,5 +1,5 @@ -import { Module } from "@nestjs/common"; -import { PrismaService } from "./prisma.service.js"; +import { Module } from '@nestjs/common'; +import { PrismaService } from './prisma.service.js'; @Module({ providers: [PrismaService], diff --git a/api/src/prisma/prisma.service.ts b/api/src/prisma/prisma.service.ts index 96a9bd6..359f950 100644 --- a/api/src/prisma/prisma.service.ts +++ b/api/src/prisma/prisma.service.ts @@ -1,5 +1,5 @@ -import { Injectable, OnModuleInit } from "@nestjs/common"; -import { PrismaClient } from "@prisma/client"; +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { diff --git a/api/src/s3/s3.module.ts b/api/src/s3/s3.module.ts index 772a2bd..57a7c6f 100644 --- a/api/src/s3/s3.module.ts +++ b/api/src/s3/s3.module.ts @@ -1,5 +1,5 @@ -import { Module } from "@nestjs/common"; -import { S3Service } from "./s3.service.js"; +import { Module } from '@nestjs/common'; +import { S3Service } from './s3.service.js'; @Module({ providers: [S3Service], diff --git a/api/src/s3/s3.service.ts b/api/src/s3/s3.service.ts index 46b86ac..fcb1c9d 100644 --- a/api/src/s3/s3.service.ts +++ b/api/src/s3/s3.service.ts @@ -1,5 +1,5 @@ -import fs from "node:fs"; -import Bluebird from "bluebird"; +import fs from 'node:fs'; +import Bluebird from 'bluebird'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { @@ -10,7 +10,7 @@ import { PutObjectCommandOutput, S3Client, StorageClass, -} from "@aws-sdk/client-s3"; +} from '@aws-sdk/client-s3'; import { S3Config } from '../config/config.interface.js'; @Injectable() @@ -22,7 +22,7 @@ export class S3Service { private readonly storageClass: string; public constructor(configService: ConfigService) { - const config = configService.get("s3")!; + const config = configService.get('s3')!; this.client = new S3Client({ region: config.region, @@ -40,7 +40,7 @@ export class S3Service { public upload(path: string, dest: string): Promise { return Bluebird.race([ Bluebird.delay(10 * 60 * 1_000).then(() => { - throw new Error("upload timeout"); + throw new Error('upload timeout'); }), this.client.send( new PutObjectCommand({ diff --git a/api/src/schema.gql b/api/src/schema.gql index ebc503e..2969254 100644 --- a/api/src/schema.gql +++ b/api/src/schema.gql @@ -186,13 +186,19 @@ type Node { longitude: Float! } -"""Buffer""" +""" +Buffer +""" scalar Bytes -"""BigInt""" +""" +BigInt +""" scalar BigInt -"""Decimal""" +""" +Decimal +""" scalar Decimal type Query { @@ -222,4 +228,4 @@ enum DeviceType { type Subscription { walletMovement(address: Bytes!): String! walletTransaction(address: Bytes!): Transaction! -} \ No newline at end of file +} diff --git a/api/src/stats/constants.ts b/api/src/stats/constants.ts index 950d607..9587542 100644 --- a/api/src/stats/constants.ts +++ b/api/src/stats/constants.ts @@ -1,2 +1,2 @@ -export const STATS_CACHE_KEY = "__0L_CACHED_STATS__"; -export const ACCOUNTS_STATS_CACHE_KEY = "__0L_CACHED_ACCOUNTS_STATS__"; +export const STATS_CACHE_KEY = '__0L_CACHED_STATS__'; +export const ACCOUNTS_STATS_CACHE_KEY = '__0L_CACHED_ACCOUNTS_STATS__'; diff --git a/api/src/stats/stats.controller.ts b/api/src/stats/stats.controller.ts index 5ff0d7d..19208a0 100644 --- a/api/src/stats/stats.controller.ts +++ b/api/src/stats/stats.controller.ts @@ -1,17 +1,12 @@ -import { - Controller, - Get, - Res, - ServiceUnavailableException, -} from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; -import { Response } from "express"; +import { Controller, Get, Res, ServiceUnavailableException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Response } from 'express'; -import { StatsService } from "./stats.service.js"; -import { redisClient } from "../redis/redis.service.js"; -import { STATS_CACHE_KEY, ACCOUNTS_STATS_CACHE_KEY } from "./constants.js"; +import { StatsService } from './stats.service.js'; +import { redisClient } from '../redis/redis.service.js'; +import { STATS_CACHE_KEY, ACCOUNTS_STATS_CACHE_KEY } from './constants.js'; -@Controller("stats") +@Controller('stats') export class StatsController { private readonly cacheEnabled: boolean; @@ -19,12 +14,12 @@ export class StatsController { private readonly statsService: StatsService, config: ConfigService, ) { - this.cacheEnabled = config.get("cacheEnabled")!; + this.cacheEnabled = config.get('cacheEnabled')!; } @Get() public async getStats(@Res() res: Response) { - res.set("Content-Type", "application/json"); + res.set('Content-Type', 'application/json'); // Check if caching is enabled and the query is not present if (this.cacheEnabled) { @@ -33,28 +28,28 @@ export class StatsController { res.send(cachedStats); return; } - throw new ServiceUnavailableException("Cache not ready"); + throw new ServiceUnavailableException('Cache not ready'); } const stats = await this.statsService.getStats(); res.send(stats); } - @Get("/total-supply") + @Get('/total-supply') public async getTotalSupply(@Res() res: Response) { const totalSupply = await this.statsService.getTotalSupply(); res.send({ totalSupply }); } - @Get("/circulating-supply") + @Get('/circulating-supply') public async getCirculatingSupply(@Res() res: Response) { const circulatingSupply = await this.statsService.getCirculatingSupply(); res.send({ circulatingSupply }); } - @Get("/accounts-stats") + @Get('/accounts-stats') public async getAccountsStats(@Res() res: Response) { - res.set("Content-Type", "application/json"); + res.set('Content-Type', 'application/json'); // Check if caching is enabled and the query is not present if (this.cacheEnabled) { @@ -63,7 +58,7 @@ export class StatsController { res.send(cachedStats); return; } - throw new ServiceUnavailableException("Cache not ready"); + throw new ServiceUnavailableException('Cache not ready'); } const accountsStats = await this.statsService.getAccountsStats(); diff --git a/api/src/stats/stats.module.ts b/api/src/stats/stats.module.ts index a6abc4f..8556464 100644 --- a/api/src/stats/stats.module.ts +++ b/api/src/stats/stats.module.ts @@ -1,13 +1,13 @@ -import { Module } from "@nestjs/common"; -import { BullModule } from "@nestjs/bullmq"; +import { Module } from '@nestjs/common'; +import { BullModule } from '@nestjs/bullmq'; -import { StatsService } from "./stats.service.js"; -import { StatsController } from "./stats.controller.js"; -import { ClickhouseModule } from "../clickhouse/clickhouse.module.js"; -import { OlModule } from "../ol/ol.module.js"; -import { redisClient } from "../redis/redis.service.js"; -import loadConfig from "../config/config.js"; -import { StatsProcessor } from "./stats.processor.js"; +import { StatsService } from './stats.service.js'; +import { StatsController } from './stats.controller.js'; +import { ClickhouseModule } from '../clickhouse/clickhouse.module.js'; +import { OlModule } from '../ol/ol.module.js'; +import { redisClient } from '../redis/redis.service.js'; +import loadConfig from '../config/config.js'; +import { StatsProcessor } from './stats.processor.js'; const config = loadConfig(); @@ -17,7 +17,7 @@ const config = loadConfig(); OlModule, BullModule.registerQueue({ - name: "stats", + name: 'stats', connection: redisClient, }), ], diff --git a/api/src/stats/stats.processor.ts b/api/src/stats/stats.processor.ts index fc01e56..dd9379e 100644 --- a/api/src/stats/stats.processor.ts +++ b/api/src/stats/stats.processor.ts @@ -1,16 +1,16 @@ -import _ from "lodash"; -import { InjectQueue, Processor, WorkerHost } from "@nestjs/bullmq"; -import { OnModuleInit } from "@nestjs/common"; -import { Job, Queue } from "bullmq"; +import _ from 'lodash'; +import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; +import { OnModuleInit } from '@nestjs/common'; +import { Job, Queue } from 'bullmq'; -import { StatsService } from "./stats.service.js"; -import { redisClient } from "../redis/redis.service.js"; -import { STATS_CACHE_KEY, ACCOUNTS_STATS_CACHE_KEY } from "./constants.js"; +import { StatsService } from './stats.service.js'; +import { redisClient } from '../redis/redis.service.js'; +import { STATS_CACHE_KEY, ACCOUNTS_STATS_CACHE_KEY } from './constants.js'; -@Processor("stats") +@Processor('stats') export class StatsProcessor extends WorkerHost implements OnModuleInit { public constructor( - @InjectQueue("stats") + @InjectQueue('stats') private readonly statsQueue: Queue, private readonly statsService: StatsService, @@ -19,7 +19,7 @@ export class StatsProcessor extends WorkerHost implements OnModuleInit { } public async onModuleInit() { - await this.statsQueue.add("updateStats", undefined, { + await this.statsQueue.add('updateStats', undefined, { repeat: { every: 60 * 60 * 2 * 1_000, // 2 hours }, @@ -33,7 +33,7 @@ export class StatsProcessor extends WorkerHost implements OnModuleInit { public async process(job: Job) { switch (job.name) { - case "updateStats": + case 'updateStats': await this.updateStats(); break; @@ -47,9 +47,6 @@ export class StatsProcessor extends WorkerHost implements OnModuleInit { await redisClient.set(STATS_CACHE_KEY, JSON.stringify(stats)); const accountsStats = await this.statsService.getAccountsStats(); - await redisClient.set( - ACCOUNTS_STATS_CACHE_KEY, - JSON.stringify(accountsStats), - ); + await redisClient.set(ACCOUNTS_STATS_CACHE_KEY, JSON.stringify(accountsStats)); } } diff --git a/api/src/stats/stats.service.ts b/api/src/stats/stats.service.ts index 06d12e5..87f37c4 100644 --- a/api/src/stats/stats.service.ts +++ b/api/src/stats/stats.service.ts @@ -1,6 +1,6 @@ -import { Inject, Injectable } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; -import axios from "axios"; +import { Inject, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import axios from 'axios'; import { Stats, @@ -11,12 +11,12 @@ import { BinRange, BalanceItem, SupplyStats, -} from "./types.js"; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; -import { OlService } from "../ol/ol.service.js"; -import { ICommunityWalletsService } from "../ol/community-wallets/interfaces.js"; -import { Types } from "../types.js"; -import _ from "lodash"; +} from './types.js'; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; +import { OlService } from '../ol/ol.service.js'; +import { ICommunityWalletsService } from '../ol/community-wallets/interfaces.js'; +import { Types } from '../types.js'; +import _ from 'lodash'; @Injectable() export class StatsService { @@ -31,7 +31,7 @@ export class StatsService { config: ConfigService, ) { - this.dataApiHost = config.get("dataApiHost")!; + this.dataApiHost = config.get('dataApiHost')!; } public async getCirculatingSupply(): Promise { @@ -45,91 +45,77 @@ export class StatsService { } public async getStats(): Promise { - console.time("getSupplyStats"); + console.time('getSupplyStats'); const supplyStats = await this.olService.getSupplyStats(); - console.timeEnd("getSupplyStats"); + console.timeEnd('getSupplyStats'); - console.time("getTotalSupply"); + console.time('getTotalSupply'); const totalSupply: number = supplyStats.totalSupply; - console.timeEnd("getTotalSupply"); + console.timeEnd('getTotalSupply'); - console.time("getSlowWalletsCountOverTime"); + console.time('getSlowWalletsCountOverTime'); const slowWalletsCountOverTime = await this.getSlowWalletsCountOverTime(); - console.timeEnd("getSlowWalletsCountOverTime"); + console.timeEnd('getSlowWalletsCountOverTime'); - console.time("getBurnsOverTime"); + console.time('getBurnsOverTime'); const burnOverTime = await this.getBurnsOverTime(); - console.timeEnd("getBurnsOverTime"); + console.timeEnd('getBurnsOverTime'); - console.time("getAccountsOnChainOverTime"); + console.time('getAccountsOnChainOverTime'); const accountsOnChainOverTime = await this.getAccountsOnChainOverTime(); - console.timeEnd("getAccountsOnChainOverTime"); + console.timeEnd('getAccountsOnChainOverTime'); - console.time("getSupplyAndCapital"); + console.time('getSupplyAndCapital'); const supplyAndCapital = await this.getSupplyAndCapital(supplyStats); - console.timeEnd("getSupplyAndCapital"); + console.timeEnd('getSupplyAndCapital'); - console.time("getCommunityWalletsBalanceBreakdown"); - const communityWalletsBalanceBreakdown = - await this.getCommunityWalletsBalanceBreakdown(); - console.timeEnd("getCommunityWalletsBalanceBreakdown"); + console.time('getCommunityWalletsBalanceBreakdown'); + const communityWalletsBalanceBreakdown = await this.getCommunityWalletsBalanceBreakdown(); + console.timeEnd('getCommunityWalletsBalanceBreakdown'); - console.time("getLastEpochTotalUnlockedAmount"); - const lastEpochTotalUnlockedAmount = - await this.getLastEpochTotalUnlockedAmount(); - console.timeEnd("getLastEpochTotalUnlockedAmount"); + console.time('getLastEpochTotalUnlockedAmount'); + const lastEpochTotalUnlockedAmount = await this.getLastEpochTotalUnlockedAmount(); + console.timeEnd('getLastEpochTotalUnlockedAmount'); - console.time("getPOFValues"); + console.time('getPOFValues'); const pofValues = await this.getPOFValues(); // Empty table? - console.timeEnd("getPOFValues"); + console.timeEnd('getPOFValues'); - console.time("getLiquidSupplyConcentration"); + console.time('getLiquidSupplyConcentration'); const liquidSupplyConcentration = await this.getLiquidSupplyConcentration(); - console.timeEnd("getLiquidSupplyConcentration"); + console.timeEnd('getLiquidSupplyConcentration'); - console.time("calculateLiquidityConcentrationLocked"); - const lockedSupplyConcentration = - await this.calculateLiquidityConcentrationLocked(); - console.timeEnd("calculateLiquidityConcentrationLocked"); + console.time('calculateLiquidityConcentrationLocked'); + const lockedSupplyConcentration = await this.calculateLiquidityConcentrationLocked(); + console.timeEnd('calculateLiquidityConcentrationLocked'); - console.time("getTopUnlockedBalanceWallets"); - const topAccounts = await this.getTopUnlockedBalanceWallets( - 100, - supplyStats.circulatingSupply, - ); - console.timeEnd("getTopUnlockedBalanceWallets"); + console.time('getTopUnlockedBalanceWallets'); + const topAccounts = await this.getTopUnlockedBalanceWallets(100, supplyStats.circulatingSupply); + console.timeEnd('getTopUnlockedBalanceWallets'); // calculate KPIS // circulating const circulatingSupply = { nominal: parseFloat(supplyStats.circulatingSupply.toFixed(3)), - percentage: parseFloat( - ((supplyStats.circulatingSupply / totalSupply) * 100).toFixed(3), - ), + percentage: parseFloat(((supplyStats.circulatingSupply / totalSupply) * 100).toFixed(3)), }; // CW const communityWalletsBalance = { nominal: parseFloat(supplyStats.cwSupply.toFixed(3)), - percentage: parseFloat( - ((supplyStats.cwSupply / totalSupply) * 100).toFixed(3), - ), + percentage: parseFloat(((supplyStats.cwSupply / totalSupply) * 100).toFixed(3)), }; // Locked const currentLockedOnSlowWallets = { nominal: parseFloat(supplyStats.slowLockedSupply.toFixed(3)), - percentage: parseFloat( - ((supplyStats.slowLockedSupply / totalSupply) * 100).toFixed(3), - ), + percentage: parseFloat(((supplyStats.slowLockedSupply / totalSupply) * 100).toFixed(3)), }; // Validators escrow const infrastructureEscrow = { nominal: parseFloat(supplyStats.infraEscrowSupply.toFixed(3)), - percentage: parseFloat( - ((supplyStats.infraEscrowSupply / totalSupply) * 100).toFixed(3), - ), + percentage: parseFloat(((supplyStats.infraEscrowSupply / totalSupply) * 100).toFixed(3)), }; const totalBurned = { @@ -138,14 +124,9 @@ export class StatsService { }; const lastEpochReward = { - nominal: - pofValues.nominalRewardOverTime[ - pofValues.nominalRewardOverTime.length - 1 - ].value, + nominal: pofValues.nominalRewardOverTime[pofValues.nominalRewardOverTime.length - 1].value, percentage: - (pofValues.nominalRewardOverTime[ - pofValues.nominalRewardOverTime.length - 1 - ].value / + (pofValues.nominalRewardOverTime[pofValues.nominalRewardOverTime.length - 1].value / totalSupply) * 100, }; @@ -172,8 +153,7 @@ export class StatsService { circulatingSupply, totalBurned, communityWalletsBalance, - currentSlowWalletsCount: - slowWalletsCountOverTime[slowWalletsCountOverTime.length - 1].value, + currentSlowWalletsCount: slowWalletsCountOverTime[slowWalletsCountOverTime.length - 1].value, currentLockedOnSlowWallets, lastEpochTotalUnlockedAmount: { nominal: lastEpochTotalUnlockedAmount, @@ -181,8 +161,7 @@ export class StatsService { }, lastEpochReward, currentClearingBid: - pofValues.clearingBidOverTime[pofValues.clearingBidOverTime.length - 1] - .value / 10, + pofValues.clearingBidOverTime[pofValues.clearingBidOverTime.length - 1].value / 10, infrastructureEscrow, lockedCoins, }; @@ -233,7 +212,7 @@ export class StatsService { query_params: { addresses, }, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ balance: number; @@ -247,55 +226,55 @@ export class StatsService { // List of addresses and their names const addressNames = [ { - address: "FBE8DA53C92CEEEB40D8967EC033A0FB", - name: "Community development", + address: 'FBE8DA53C92CEEEB40D8967EC033A0FB', + name: 'Community development', }, { - address: "2640CD6D652AC94DC5F0963DCC00BCC7", - name: "Engineering Fund, tool-scrubbers-guild", + address: '2640CD6D652AC94DC5F0963DCC00BCC7', + name: 'Engineering Fund, tool-scrubbers-guild', }, { - address: "C906F67F626683B77145D1F20C1A753B", - name: "The Iqlusion Engineering Program", + address: 'C906F67F626683B77145D1F20C1A753B', + name: 'The Iqlusion Engineering Program', }, { - address: "3A6C51A0B786D644590E8A21591FA8E2", - name: "FTW: Ongoing Full-Time Workers Program", + address: '3A6C51A0B786D644590E8A21591FA8E2', + name: 'FTW: Ongoing Full-Time Workers Program', }, - { address: "BCA50D10041FA111D1B44181A264A599", name: "A Good List" }, - { address: "2B0E8325DEA5BE93D856CFDE2D0CBA12", name: "Tip Jar" }, + { address: 'BCA50D10041FA111D1B44181A264A599', name: 'A Good List' }, + { address: '2B0E8325DEA5BE93D856CFDE2D0CBA12', name: 'Tip Jar' }, { - address: "19E966BFA4B32CE9B7E23721B37B96D2", - name: "Social Infrastructure Program", + address: '19E966BFA4B32CE9B7E23721B37B96D2', + name: 'Social Infrastructure Program', }, { - address: "B31BD7796BC113013A2BF6C3953305FD", - name: "Danish Red Cross Humanitarian Fund", + address: 'B31BD7796BC113013A2BF6C3953305FD', + name: 'Danish Red Cross Humanitarian Fund', }, { - address: "BC25F79FEF8A981BE4636AC1A2D6F587", - name: "Application Studio", + address: 'BC25F79FEF8A981BE4636AC1A2D6F587', + name: 'Application Studio', }, - { address: "2057BCFB0189B7FD0ABA7244BA271661", name: "Moonshot Program" }, + { address: '2057BCFB0189B7FD0ABA7244BA271661', name: 'Moonshot Program' }, { - address: "F605FE7F787551EEA808EE9ACDB98897", - name: "Human Rewards Program", + address: 'F605FE7F787551EEA808EE9ACDB98897', + name: 'Human Rewards Program', }, { - address: "C19C06A592911ED31C4100E9FB63AD7B", - name: "RxC Research and Experimentation", + address: 'C19C06A592911ED31C4100E9FB63AD7B', + name: 'RxC Research and Experimentation', }, { - address: "1367B68C86CB27FA7215D9F75A26EB8F", - name: "University of Toronto MSRG", + address: '1367B68C86CB27FA7215D9F75A26EB8F', + name: 'University of Toronto MSRG', }, { - address: "BB6926434D1497A559E4F0487F79434F", - name: "Deep Technology Innovation Program", + address: 'BB6926434D1497A559E4F0487F79434F', + name: 'Deep Technology Innovation Program', }, { - address: "87DC2E497AC6EDAB21511333A421E5A5", - name: "Working Group Key Roles", + address: '87DC2E497AC6EDAB21511333A421E5A5', + name: 'Working Group Key Roles', }, ]; @@ -335,7 +314,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ @@ -355,10 +334,10 @@ export class StatsService { // Clean and parse the rows const cleanedRows = rows.map((row) => ({ - version: parseInt(String(row.version).replace("\n", ""), 10), - nominalReward: parseFloat(String(row.nominalReward).replace("\n", "")), - netReward: parseFloat(String(row.netReward).replace("\n", "")), - clearingBid: parseFloat(String(row.clearingBid).replace("\n", "")), + version: parseInt(String(row.version).replace('\n', ''), 10), + nominalReward: parseFloat(String(row.nominalReward).replace('\n', '')), + netReward: parseFloat(String(row.netReward).replace('\n', '')), + clearingBid: parseFloat(String(row.clearingBid).replace('\n', '')), })); // Extract versions and convert them to timestamps @@ -375,9 +354,7 @@ export class StatsService { // Helper to convert version to timestamp const convertVersionToTimestamp = (version: number) => { - const timestampEntry = allTimestampMappings.find( - (entry) => entry.version === version, - ); + const timestampEntry = allTimestampMappings.find((entry) => entry.version === version); return timestampEntry ? Math.floor(timestampEntry.timestamp) : 0; }; @@ -403,7 +380,7 @@ export class StatsService { netRewardOverTime, }; } catch (error) { - console.error("Error in getPOFValues:", error); + console.error('Error in getPOFValues:', error); throw error; } } @@ -420,7 +397,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ @@ -438,9 +415,7 @@ export class StatsService { const burnsOverTime = rows.map((row) => { const version = parseInt(row.version, 10); - const timestampEntry = timestampsMap.find( - (entry) => entry.version === version, - ); + const timestampEntry = timestampsMap.find((entry) => entry.version === version); const timestamp = timestampEntry ? timestampEntry.timestamp : 0; return { @@ -449,7 +424,7 @@ export class StatsService { }; }); - const baseTimestamp = new Date("2023-11-28T00:00:00Z").getTime() / 1000; + const baseTimestamp = new Date('2023-11-28T00:00:00Z').getTime() / 1000; if (burnsOverTime[0].timestamp == 0) { burnsOverTime[0].timestamp = baseTimestamp; } @@ -459,7 +434,7 @@ export class StatsService { value: item.value, })); } catch (error) { - console.error("Error in getBurnsOverTime:", error); + console.error('Error in getBurnsOverTime:', error); throw error; } } @@ -485,20 +460,17 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ locked_balance: number }>(); // Sum the locked Amount - const totalLockedAmount = rows.reduce( - (acc, row) => acc + row.locked_balance, - 0, - ); + const totalLockedAmount = rows.reduce((acc, row) => acc + row.locked_balance, 0); return totalLockedAmount; } catch (error) { - console.error("Error in getSlowWalletsUnlockedAmount:", error); + console.error('Error in getSlowWalletsUnlockedAmount:', error); throw error; } } @@ -516,7 +488,7 @@ export class StatsService { const slowWalletResultSet = await this.clickhouseService.client.query({ query: slowWalletQuery, - format: "JSONEachRow", + format: 'JSONEachRow', }); const slowWalletRows = await slowWalletResultSet.json<{ @@ -536,7 +508,7 @@ export class StatsService { const balanceResults: { address: string; latest_balance: number }[] = []; for (const chunk of addressChunks) { - const formattedAddresses = chunk.map((addr) => `'${addr}'`).join(","); + const formattedAddresses = chunk.map((addr) => `'${addr}'`).join(','); const balanceQuery = ` SELECT hex(address) AS address, @@ -548,7 +520,7 @@ export class StatsService { const balanceResultSet = await this.clickhouseService.client.query({ query: balanceQuery, - format: "JSONEachRow", + format: 'JSONEachRow', }); const balanceRows = await balanceResultSet.json<{ @@ -559,9 +531,7 @@ export class StatsService { } // Create a map of address to latest balance - const balanceMap = new Map( - balanceResults.map((row) => [row.address, row.latest_balance]), - ); + const balanceMap = new Map(balanceResults.map((row) => [row.address, row.latest_balance])); // Combine data from both queries const lockedBalances = slowWalletRows.map((row) => { @@ -585,7 +555,7 @@ export class StatsService { const totalUnlockedAmount = count * 35000; return totalUnlockedAmount; } catch (error) { - console.error("Error in getLastEpochTotalUnlockedAmount:", error); + console.error('Error in getLastEpochTotalUnlockedAmount:', error); throw error; } } @@ -603,7 +573,7 @@ export class StatsService { private async mapVersionsToTimestamps( versions: number[], ): Promise<{ version: number; timestamp: number }[]> { - const versionsString = versions.join(", "); + const versionsString = versions.join(', '); const query = ` WITH @@ -677,7 +647,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query, query_params: { versions }, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ @@ -701,7 +671,7 @@ export class StatsService { FROM "slow_wallet_list" ORDER BY "version" ASC `, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ version: string; @@ -709,7 +679,7 @@ export class StatsService { }>(); if (!rows.length) { - console.warn("No data found for slow wallets over time."); + console.warn('No data found for slow wallets over time.'); return []; } @@ -719,9 +689,7 @@ export class StatsService { const slowWalletsOverTime = rows.map((row) => { const version = parseInt(row.version, 10); - const timestampEntry = timestampsMap.find( - (entry) => entry.version === version, - ); + const timestampEntry = timestampsMap.find((entry) => entry.version === version); const timestamp = timestampEntry ? timestampEntry.timestamp : 0; return { @@ -730,7 +698,7 @@ export class StatsService { }; }); - const baseTimestamp = new Date("2023-11-28T00:00:00Z").getTime() / 1000; + const baseTimestamp = new Date('2023-11-28T00:00:00Z').getTime() / 1000; if (slowWalletsOverTime[0].timestamp == 0) { slowWalletsOverTime[0].timestamp = baseTimestamp; } @@ -742,7 +710,7 @@ export class StatsService { return result; } catch (error) { - console.error("Error in getSlowWalletsCountOverTime:", error); + console.error('Error in getSlowWalletsCountOverTime:', error); throw error; } } @@ -760,7 +728,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ @@ -781,17 +749,12 @@ export class StatsService { // Process each chunk concurrently const allTimestampMappings = ( - await Promise.all( - versionChunks.map((chunk) => this.mapVersionsToTimestamps(chunk)), - ) + await Promise.all(versionChunks.map((chunk) => this.mapVersionsToTimestamps(chunk))) ).flat(); // Use a Map for faster version-to-timestamp lookup const versionToTimestampMap = new Map( - allTimestampMappings.map(({ version, timestamp }) => [ - version, - timestamp, - ]), + allTimestampMappings.map(({ version, timestamp }) => [version, timestamp]), ); // Initialize the result array and a count for accounts with timestamp > 0 @@ -833,7 +796,7 @@ export class StatsService { return accountsOverTime; } catch (error) { - console.error("Error in getAccountsOnChainOverTime:", error); + console.error('Error in getAccountsOnChainOverTime:', error); throw error; } } @@ -868,7 +831,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ @@ -888,16 +851,11 @@ export class StatsService { const chunkSize = 1000; const versionChunks = this.chunkArray(versions, chunkSize); const allTimestampMappings = ( - await Promise.all( - versionChunks.map((chunk) => this.mapVersionsToTimestamps(chunk)), - ) + await Promise.all(versionChunks.map((chunk) => this.mapVersionsToTimestamps(chunk))) ).flat(); const versionToTimestampMap = new Map( - allTimestampMappings.map(({ version, timestamp }) => [ - version, - timestamp, - ]), + allTimestampMappings.map(({ version, timestamp }) => [version, timestamp]), ); const now = Math.floor(Date.now() / 1000); @@ -930,7 +888,7 @@ export class StatsService { last90Days: seenAddressesLast90Days.size, }; } catch (error) { - console.error("Error in getActiveAddressesCount:", error); + console.error('Error in getActiveAddressesCount:', error); throw error; } } @@ -945,7 +903,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ unique_accounts: number }[]>(); @@ -953,11 +911,11 @@ export class StatsService { if (rows.length === 0) { return 0; } - const uniqueAccountsCount = Number(rows[0]["unique_accounts"]); + const uniqueAccountsCount = Number(rows[0]['unique_accounts']); return uniqueAccountsCount; } catch (error) { - console.error("Error in getTotalUniqueAccounts:", error); + console.error('Error in getTotalUniqueAccounts:', error); throw error; } } @@ -976,20 +934,20 @@ export class StatsService { try { // Organize the values into the specified structure const supplyAllocation = [ - { name: "Community Wallets", value: communityWalletsBalances }, - { name: "Locked", value: slowLocked }, - { name: "Infrastructure escrow", value: infraEscrowBalance }, - { name: "Circulating", value: circulating }, + { name: 'Community Wallets', value: communityWalletsBalances }, + { name: 'Locked', value: slowLocked }, + { name: 'Infrastructure escrow', value: infraEscrowBalance }, + { name: 'Circulating', value: circulating }, ]; const individualsCapital = [ - { name: "Locked", value: slowLocked }, - { name: "Circulating", value: circulating }, + { name: 'Locked', value: slowLocked }, + { name: 'Circulating', value: circulating }, ]; const communityCapital = [ - { name: "Community Wallets", value: communityWalletsBalances }, - { name: "Infrastructure escrow", value: infraEscrowBalance }, + { name: 'Community Wallets', value: communityWalletsBalances }, + { name: 'Infrastructure escrow', value: infraEscrowBalance }, ]; return { @@ -998,7 +956,7 @@ export class StatsService { communityCapital, }; } catch (error) { - console.error("Error in getSupplyAndCapital:", error); + console.error('Error in getSupplyAndCapital:', error); throw error; } } @@ -1009,8 +967,7 @@ export class StatsService { // Fetch community wallets to exclude const communityWallets = await this.olService.getCommunityWallets(); // Fetch slow wallets' unlocked balances - const slowWalletsUnlockedBalances = - await this.getSlowWalletsUnlockedBalances(); + const slowWalletsUnlockedBalances = await this.getSlowWalletsUnlockedBalances(); // Fetch all other wallets' balances, excluding community wallets and including adjustments for slow wallets const allWalletsBalances = await this.getAllWalletsBalances( communityWallets, @@ -1039,7 +996,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ @@ -1058,24 +1015,16 @@ export class StatsService { const versionChunks = this.chunkArray(versions, chunkSize); const allTimestampMappings = ( - await Promise.all( - versionChunks.map((chunk) => this.mapVersionsToTimestamps(chunk)), - ) + await Promise.all(versionChunks.map((chunk) => this.mapVersionsToTimestamps(chunk))) ).flat(); // Use a Map for faster version-to-timestamp lookup const versionToTimestampMap = new Map( - allTimestampMappings.map(({ version, timestamp }) => [ - version, - timestamp, - ]), + allTimestampMappings.map(({ version, timestamp }) => [version, timestamp]), ); // Create a map of address to latest balance and timestamp - const addressBalanceMap = new Map< - string, - { latest_balance: number; timestamp: number } - >(); + const addressBalanceMap = new Map(); rows.forEach((row) => { const version = row.latest_version; const timestamp = versionToTimestampMap.get(version) ?? 0; @@ -1096,7 +1045,7 @@ export class StatsService { const slowWalletResultSet = await this.clickhouseService.client.query({ query: slowWalletQuery, - format: "JSONEachRow", + format: 'JSONEachRow', }); const slowWalletRows = await slowWalletResultSet.json<{ @@ -1110,10 +1059,7 @@ export class StatsService { if (addressData) { return { address: row.address, - unlockedBalance: Math.min( - row.unlocked_balance, - addressData.latest_balance, - ), + unlockedBalance: Math.min(row.unlocked_balance, addressData.latest_balance), }; } else { return { @@ -1129,7 +1075,7 @@ export class StatsService { unlockedBalance: Math.max(0, row.unlockedBalance), })); } catch (error) { - console.error("Error in getSlowWalletsUnlockedBalances:", error); + console.error('Error in getSlowWalletsUnlockedBalances:', error); throw error; } } @@ -1144,9 +1090,7 @@ export class StatsService { } // Convert community wallets to a Set for easy lookup - const communityAddresses = new Set( - communityWallets.map((wallet) => wallet.toUpperCase()), - ); + const communityAddresses = new Set(communityWallets.map((wallet) => wallet.toUpperCase())); // Convert slow wallets to a map for easy access const slowWalletsMap = new Map( @@ -1169,7 +1113,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ @@ -1203,16 +1147,14 @@ export class StatsService { }); // Convert the map to an array - const result = Array.from(addressBalanceMap.entries()).map( - ([address, balance]) => ({ - address, - balance, - }), - ); + const result = Array.from(addressBalanceMap.entries()).map(([address, balance]) => ({ + address, + balance, + })); return result; } catch (error) { - console.error("Error in getAllWalletsBalances:", error); + console.error('Error in getAllWalletsBalances:', error); throw error; } } @@ -1233,8 +1175,7 @@ export class StatsService { balances.forEach((item) => { const binIndex = ranges.findIndex( (range) => - item.balance >= range.min && - (item.balance <= range.max || range.max === Infinity), + item.balance >= range.min && (item.balance <= range.max || range.max === Infinity), ); if (binIndex !== -1) { bins[binIndex].value += 1; @@ -1270,10 +1211,8 @@ export class StatsService { // Main method to calculate liquidity concentration for locked balances private async calculateLiquidityConcentrationLocked(): Promise { const lockedBalances = await this.getLockedBalancesForSlowWallets(); - const accountsLocked = - this.calculateLockedBalancesConcentration(lockedBalances); - const avgTotalVestingTime = - this.calculateAvgTotalVestingTime(accountsLocked); + const accountsLocked = this.calculateLockedBalancesConcentration(lockedBalances); + const avgTotalVestingTime = this.calculateAvgTotalVestingTime(accountsLocked); return { accountsLocked, avgTotalVestingTime, @@ -1284,16 +1223,14 @@ export class StatsService { try { const slowWalletAddresses = await this.getSlowWalletAddresses(); - const walletsBalances = - await this.getWalletsBalances(slowWalletAddresses); + const walletsBalances = await this.getWalletsBalances(slowWalletAddresses); const unlockedBalances = await this.getSlowWalletsUnlockedBalances(); const lockedBalances = walletsBalances .map((wallet) => { const unlockedAmount = - unlockedBalances.find((u) => u.address === wallet.address) - ?.unlockedBalance || 0; + unlockedBalances.find((u) => u.address === wallet.address)?.unlockedBalance || 0; const lockedBalanceCalculation = wallet.balance - unlockedAmount; return { @@ -1305,7 +1242,7 @@ export class StatsService { return lockedBalances; } catch (error) { - console.error("Error in getLockedBalancesForSlowWallets:", error); + console.error('Error in getLockedBalancesForSlowWallets:', error); throw error; } } @@ -1319,7 +1256,7 @@ export class StatsService { const resultSet = await this.clickhouseService.client.query({ query: query, - format: "JSONEachRow", + format: 'JSONEachRow', }); const rows = await resultSet.json<{ address: string }>(); @@ -1328,14 +1265,12 @@ export class StatsService { return addresses; } catch (error) { - console.error("Error in getSlowWalletAddresses:", error); + console.error('Error in getSlowWalletAddresses:', error); throw error; } } - private calculateLockedBalancesConcentration( - lockedBalances: LockedBalance[], - ): BinRange[] { + private calculateLockedBalancesConcentration(lockedBalances: LockedBalance[]): BinRange[] { const balanceItems: BalanceItem[] = lockedBalances.map((wallet) => ({ balance: wallet.lockedBalance, })); @@ -1357,12 +1292,12 @@ export class StatsService { return bins.map((bin) => { // Extract the lower and upper bounds from the bin name let lower, upper; - if (bin.name.includes("and above")) { + if (bin.name.includes('and above')) { // For "and above" case, extract the lower bound and use it as both lower and upper for simplicity - lower = Number(bin.name.split(" ")[0]); + lower = Number(bin.name.split(' ')[0]); upper = lower; // This simplification might need adjustment based on actual data distribution } else { - [lower, upper] = bin.name.split(" - ").map(Number); + [lower, upper] = bin.name.split(' - ').map(Number); } const middleValue = (lower + (upper || lower)) / 2; @@ -1379,9 +1314,7 @@ export class StatsService { private async getTopUnlockedBalanceWallets( limit: number, circulatingSupply: number, - ): Promise< - { address: string; unlockedBalance: number; percentOfCirculating: number }[] - > { + ): Promise<{ address: string; unlockedBalance: number; percentOfCirculating: number }[]> { try { function toHexString(decimalString: string): string { return BigInt(decimalString).toString(16).toUpperCase(); @@ -1392,11 +1325,8 @@ export class StatsService { } // Get the list of community wallets - const communityWallets = - await this.communityWalletsService.getCommunityWallets(); - const communityAddresses = new Set( - communityWallets.map((wallet) => wallet.address), - ); + const communityWallets = await this.communityWalletsService.getCommunityWallets(); + const communityAddresses = new Set(communityWallets.map((wallet) => wallet.address)); // Query to get the latest balances and versions from coin_balance const coinBalanceQuery = ` @@ -1410,7 +1340,7 @@ export class StatsService { const coinBalanceResultSet = await this.clickhouseService.client.query({ query: coinBalanceQuery, - format: "JSONEachRow", + format: 'JSONEachRow', }); const coinBalanceRows: Array<{ @@ -1442,7 +1372,7 @@ export class StatsService { const slowWalletResultSet = await this.clickhouseService.client.query({ query: slowWalletQuery, - format: "JSONEachRow", + format: 'JSONEachRow', }); const slowWalletRows: Array<{ @@ -1462,19 +1392,17 @@ export class StatsService { }); // Convert the map to an array and calculate percentOfCirculating - const result = Array.from(addressBalanceMap.entries()).map( - ([address, unlockedBalance]) => ({ - address, - unlockedBalance, - percentOfCirculating: (unlockedBalance / circulatingSupply) * 100, - }), - ); + const result = Array.from(addressBalanceMap.entries()).map(([address, unlockedBalance]) => ({ + address, + unlockedBalance, + percentOfCirculating: (unlockedBalance / circulatingSupply) * 100, + })); // Sort by unlockedBalance and take the top N result.sort((a, b) => b.unlockedBalance - a.unlockedBalance); return result.slice(0, limit); } catch (error) { - console.error("Error in getTopUnlockedBalanceWallets:", error); + console.error('Error in getTopUnlockedBalanceWallets:', error); throw error; } } diff --git a/api/src/stats/types.ts b/api/src/stats/types.ts index 719b0bd..70490c8 100644 --- a/api/src/stats/types.ts +++ b/api/src/stats/types.ts @@ -21,7 +21,7 @@ export type BinRange = { name: string; value: number }; export type WalletBalance = { address: string; balance: number }; export type LockedBalance = { address: string; lockedBalance: number }; export type BalanceItem = { balance: number }; -export type RelativeValue = { nominal: number; percentage: number } +export type RelativeValue = { nominal: number; percentage: number }; export interface Stats { slowWalletsCountOverTime: TimestampValue[]; @@ -38,9 +38,11 @@ export interface Stats { accountsLocked: BinRange[]; avgTotalVestingTime: BinRange[]; }; - topAccounts: Array<{address: string; + topAccounts: Array<{ + address: string; unlockedBalance: number; - percentOfCirculating: number;}> + percentOfCirculating: number; + }>; circulatingSupply: RelativeValue; totalBurned: RelativeValue; communityWalletsBalance: RelativeValue; @@ -51,4 +53,4 @@ export interface Stats { infrastructureEscrow: RelativeValue; currentClearingBid: number; lockedCoins: [number, number][]; -} \ No newline at end of file +} diff --git a/api/src/types.ts b/api/src/types.ts index 8f7c612..8dddd2a 100644 --- a/api/src/types.ts +++ b/api/src/types.ts @@ -1,9 +1,8 @@ - export enum Types { - ITransactionsRepository = "ITransactionsRepository", - ITransactionsService = "ITransactionsService", - ITransactionsFactory = "ITransactionsFactory", - ITransaction = "ITransaction", - IOnChainTransactionsRepository = "IOnChainTransactionsRepository", - ICommunityWalletsService = "ICommunityWalletsService", -} \ No newline at end of file + ITransactionsRepository = 'ITransactionsRepository', + ITransactionsService = 'ITransactionsService', + ITransactionsFactory = 'ITransactionsFactory', + ITransaction = 'ITransaction', + IOnChainTransactionsRepository = 'IOnChainTransactionsRepository', + ICommunityWalletsService = 'ICommunityWalletsService', +} diff --git a/api/src/utils.ts b/api/src/utils.ts index 71897a6..2a45bae 100644 --- a/api/src/utils.ts +++ b/api/src/utils.ts @@ -1,17 +1,17 @@ -import { rm } from "node:fs/promises"; -import os from "node:os"; -import pathUtil from "node:path"; -import fs from "node:fs"; +import { rm } from 'node:fs/promises'; +import os from 'node:os'; +import pathUtil from 'node:path'; +import fs from 'node:fs'; -import { sha3_256 } from "@noble/hashes/sha3"; +import { sha3_256 } from '@noble/hashes/sha3'; import { Deserializer, Serializer, SignedTransaction, TransactionPayloadEntryFunction, -} from "@aptos-labs/ts-sdk"; -import * as d3 from "d3-array"; -import BN from "bn.js"; +} from '@aptos-labs/ts-sdk'; +import * as d3 from 'd3-array'; +import BN from 'bn.js'; export const cleanUp = async (...files: string[]) => { for (const file of files) { @@ -20,22 +20,19 @@ export const cleanUp = async (...files: string[]) => { }; export const createTmpDir = async (): Promise => { - return await fs.promises.mkdtemp(pathUtil.join(os.tmpdir(), "explorer-api-")); + return await fs.promises.mkdtemp(pathUtil.join(os.tmpdir(), 'explorer-api-')); }; export function parseHexString(str: string): Uint8Array { let cleanStr = str; // strip 0x prefix - if ( - cleanStr.length >= 2 && - cleanStr[0] === "0" && - (cleanStr[1] === "x" || cleanStr[1] === "X") - ) { + if (cleanStr.length >= 2 && cleanStr[0] === '0' && (cleanStr[1] === 'x' || cleanStr[1] === 'X')) { cleanStr = cleanStr.substring(2); } - if ((cleanStr.length & 1) === 1) { // even length + if ((cleanStr.length & 1) === 1) { + // even length cleanStr = `0${cleanStr}`; } @@ -46,31 +43,27 @@ export function parseHexString(str: string): Uint8Array { } return new Uint8Array(Buffer.from(cleanStr, 'hex')); -}; +} export const parseAddress = (address: string): Buffer => { let addr = address; // strip 0x prefix - if ( - addr.length >= 2 && - addr[0] === "0" && - (addr[1] === "x" || addr[1] === "X") - ) { + if (addr.length >= 2 && addr[0] === '0' && (addr[1] === 'x' || addr[1] === 'X')) { addr = addr.substring(2); } if (addr.length > 64) { - throw new Error("Invalid address length"); + throw new Error('Invalid address length'); } if (addr.length <= 32) { - addr = addr.padStart(32, "0"); + addr = addr.padStart(32, '0'); } else if (addr.length < 64) { - addr = addr.padStart(64, "0"); + addr = addr.padStart(64, '0'); } - return Buffer.from(addr, "hex"); + return Buffer.from(addr, 'hex'); }; export const bnBisect = d3.bisector((a: BN, b: BN) => { @@ -89,18 +82,14 @@ export function deserializeSignedTransaction(transaction: Uint8Array): SignedTra return signedTransaction; } -export function getTransactionHash( - signedTransaction: SignedTransaction, -): Uint8Array { +export function getTransactionHash(signedTransaction: SignedTransaction): Uint8Array { const serializer = new Serializer(); signedTransaction.serialize(serializer); - if ( - signedTransaction.raw_txn.payload instanceof TransactionPayloadEntryFunction - ) { + if (signedTransaction.raw_txn.payload instanceof TransactionPayloadEntryFunction) { const txHash = sha3_256 .create() - .update(sha3_256.create().update("DIEM::Transaction").digest()) + .update(sha3_256.create().update('DIEM::Transaction').digest()) .update(new Uint8Array([0])) .update(serializer.toUint8Array()) .digest(); @@ -108,7 +97,7 @@ export function getTransactionHash( } console.error( - `unsupported transaction payload type: signedTransaction=${Buffer.from(signedTransaction.bcsToHex().toStringWithoutPrefix()).toString("hex").toUpperCase()}`, + `unsupported transaction payload type: signedTransaction=${Buffer.from(signedTransaction.bcsToHex().toStringWithoutPrefix()).toString('hex').toUpperCase()}`, ); - throw new Error("unsupported transaction payload type"); + throw new Error('unsupported transaction payload type'); } diff --git a/api/src/wallet-subscription/wallet-subscription.module.ts b/api/src/wallet-subscription/wallet-subscription.module.ts index 3f1a96d..da30f76 100644 --- a/api/src/wallet-subscription/wallet-subscription.module.ts +++ b/api/src/wallet-subscription/wallet-subscription.module.ts @@ -1,18 +1,18 @@ -import { Module, Type } from "@nestjs/common"; -import { BullModule } from "@nestjs/bullmq"; +import { Module, Type } from '@nestjs/common'; +import { BullModule } from '@nestjs/bullmq'; -import { redisClient } from "../redis/redis.service.js"; -import { WalletSubscriptionService } from "./wallet-subscription.service.js"; -import { WalletSubscriptionResolver } from "./wallet-subscription.resolver.js"; -import { PrismaModule } from "../prisma/prisma.module.js"; -import { ClickhouseModule } from "../clickhouse/clickhouse.module.js"; -import { WalletSubscriptionProcessor } from "./wallet-subscription.processor.js"; -import { FirebaseModule } from "../firebase/firebase.module.js"; +import { redisClient } from '../redis/redis.service.js'; +import { WalletSubscriptionService } from './wallet-subscription.service.js'; +import { WalletSubscriptionResolver } from './wallet-subscription.resolver.js'; +import { PrismaModule } from '../prisma/prisma.module.js'; +import { ClickhouseModule } from '../clickhouse/clickhouse.module.js'; +import { WalletSubscriptionProcessor } from './wallet-subscription.processor.js'; +import { FirebaseModule } from '../firebase/firebase.module.js'; -const roles = process.env.ROLES!.split(","); +const roles = process.env.ROLES!.split(','); const workers: Type[] = []; -if (roles.includes("wallet-subscription-processor")) { +if (roles.includes('wallet-subscription-processor')) { workers.push(WalletSubscriptionProcessor); } @@ -23,15 +23,11 @@ if (roles.includes("wallet-subscription-processor")) { FirebaseModule, BullModule.registerQueue({ - name: "wallet-subscription", + name: 'wallet-subscription', connection: redisClient, }), ], - providers: [ - WalletSubscriptionResolver, - WalletSubscriptionService, - ...workers, - ], + providers: [WalletSubscriptionResolver, WalletSubscriptionService, ...workers], exports: [WalletSubscriptionService], }) export class WalletSubscriptionModule {} diff --git a/api/src/wallet-subscription/wallet-subscription.processor.ts b/api/src/wallet-subscription/wallet-subscription.processor.ts index ba96711..1ad6c59 100644 --- a/api/src/wallet-subscription/wallet-subscription.processor.ts +++ b/api/src/wallet-subscription/wallet-subscription.processor.ts @@ -1,27 +1,21 @@ -import { Processor, WorkerHost } from "@nestjs/bullmq"; -import { Job } from "bullmq"; -import { WalletSubscriptionService } from "./wallet-subscription.service.js"; +import { Processor, WorkerHost } from '@nestjs/bullmq'; +import { Job } from 'bullmq'; +import { WalletSubscriptionService } from './wallet-subscription.service.js'; export interface ReleaseVersionJobData { version: string; } -@Processor("wallet-subscription") +@Processor('wallet-subscription') export class WalletSubscriptionProcessor extends WorkerHost { - public constructor( - private readonly walletSubscriptionService: WalletSubscriptionService, - ) { + public constructor(private readonly walletSubscriptionService: WalletSubscriptionService) { super(); } - public async process( - job: Job, - ): Promise { + public async process(job: Job): Promise { switch (job.name) { - case "releaseVersion": - await this.walletSubscriptionService.processReleaseVersionJob( - job.data.version, - ); + case 'releaseVersion': + await this.walletSubscriptionService.processReleaseVersionJob(job.data.version); break; default: diff --git a/api/src/wallet-subscription/wallet-subscription.resolver.ts b/api/src/wallet-subscription/wallet-subscription.resolver.ts index 975aec3..9cce0ed 100644 --- a/api/src/wallet-subscription/wallet-subscription.resolver.ts +++ b/api/src/wallet-subscription/wallet-subscription.resolver.ts @@ -1,13 +1,13 @@ -import { Args, Mutation, Resolver, registerEnumType } from "@nestjs/graphql"; -import { PrismaService } from "../prisma/prisma.service.js"; -import { GraphQLError } from "graphql"; +import { Args, Mutation, Resolver, registerEnumType } from '@nestjs/graphql'; +import { PrismaService } from '../prisma/prisma.service.js'; +import { GraphQLError } from 'graphql'; export enum DeviceType { - IOS = "IOS", - ANDROID = "ANDROID", + IOS = 'IOS', + ANDROID = 'ANDROID', } -registerEnumType(DeviceType, { name: "DeviceType" }); +registerEnumType(DeviceType, { name: 'DeviceType' }); @Resolver() export class WalletSubscriptionResolver { @@ -15,20 +15,20 @@ export class WalletSubscriptionResolver { @Mutation(() => Boolean) async walletSubscribe( - @Args("walletAddress") + @Args('walletAddress') walletAddress: Buffer, - @Args("deviceToken") + @Args('deviceToken') deviceToken: string, @Args({ - name: "deviceType", + name: 'deviceType', type: () => DeviceType, }) deviceType: DeviceType, ): Promise { if (walletAddress.length !== 16 && walletAddress.length !== 32) { - throw new GraphQLError("Invalid walletAddress length. Must be 16 or 32."); + throw new GraphQLError('Invalid walletAddress length. Must be 16 or 32.'); } const res = await this.prisma.$queryRaw<[{ id: string }]>` diff --git a/api/src/wallet-subscription/wallet-subscription.service.ts b/api/src/wallet-subscription/wallet-subscription.service.ts index 486ef27..55303fe 100644 --- a/api/src/wallet-subscription/wallet-subscription.service.ts +++ b/api/src/wallet-subscription/wallet-subscription.service.ts @@ -1,15 +1,15 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable } from '@nestjs/common'; import apn from '@parse/node-apn'; -import { DeviceType } from "@prisma/client"; -import { ConfigService } from "@nestjs/config"; -import { InjectQueue } from "@nestjs/bullmq"; -import { Queue } from "bullmq"; +import { DeviceType } from '@prisma/client'; +import { ConfigService } from '@nestjs/config'; +import { InjectQueue } from '@nestjs/bullmq'; +import { Queue } from 'bullmq'; -import { ClickhouseService } from "../clickhouse/clickhouse.service.js"; -import { PrismaService } from "../prisma/prisma.service.js"; -import { ApnConfig } from "../config/config.interface.js"; -import { ReleaseVersionJobData } from "./wallet-subscription.processor.js"; -import { FirebaseService } from "../firebase/firebase.service.js"; +import { ClickhouseService } from '../clickhouse/clickhouse.service.js'; +import { PrismaService } from '../prisma/prisma.service.js'; +import { ApnConfig } from '../config/config.interface.js'; +import { ReleaseVersionJobData } from './wallet-subscription.processor.js'; +import { FirebaseService } from '../firebase/firebase.service.js'; @Injectable() export class WalletSubscriptionService { @@ -21,10 +21,10 @@ export class WalletSubscriptionService { private readonly prisma: PrismaService, private readonly firebaseService: FirebaseService, - @InjectQueue("wallet-subscription") + @InjectQueue('wallet-subscription') private readonly walletSubscriptionQueue: Queue, ) { - const apnConfig = config.get("apn"); + const apnConfig = config.get('apn'); if (apnConfig) { this.apnProvider = new apn.Provider({ production: false, @@ -38,13 +38,9 @@ export class WalletSubscriptionService { } public async releaseVersion(version: string) { - await this.walletSubscriptionQueue.add( - "releaseVersion", - { version } as ReleaseVersionJobData, - { - jobId: `__version__${version}`, - }, - ); + await this.walletSubscriptionQueue.add('releaseVersion', { version } as ReleaseVersionJobData, { + jobId: `__version__${version}`, + }); } public async processReleaseVersionJob(version: string) { @@ -82,14 +78,12 @@ export class WalletSubscriptionService { ) `, query_params: { version }, - format: "JSONCompact", + format: 'JSONCompact', }); const rows = await result.json<[number, string]>(); const balances = rows.data; - const addresses = balances.map(([_, address]) => - Buffer.from(address, "hex"), - ); + const addresses = balances.map(([_, address]) => Buffer.from(address, 'hex')); const subscriptions = await this.prisma.walletSubscription.findMany({ where: { @@ -103,45 +97,44 @@ export class WalletSubscriptionService { }); for (const subscription of subscriptions) { - const walletAddress = subscription.walletAddress - .toString("hex") - .toUpperCase(); + const walletAddress = subscription.walletAddress.toString('hex').toUpperCase(); const slowWallet = balances.find((it) => it[1] === walletAddress)!; switch (subscription.device.type) { - case DeviceType.IOS: { - const note = new apn.Notification(); - note.expiry = Math.floor(Date.now() / 1e3) + 3_600; // Expires 1 hour from now. - note.alert = `New balance Ƚ ${slowWallet[0].toLocaleString("en-US")} on version ${BigInt(version).toLocaleString("en-US")}`; - note.payload = { - messageFrom: "John Appleseed", - }; - note.topic = "app.postero.postero"; - - const res = await this.apnProvider.send( - note, - subscription.device.token, - ); - console.log("res", res); - } break; - - case DeviceType.ANDROID: { - const { app } = this.firebaseService; - - if (app) { - const res = await app.messaging().send({ - token: subscription.device.token, - notification: { - title: `New balance Ƚ ${slowWallet[0].toLocaleString("en-US")}`, - body: `Version ${BigInt(version).toLocaleString("en-US")}`, - }, - data: { - story_id: "story_12345", - }, - }); - console.log("res", res); + case DeviceType.IOS: + { + const note = new apn.Notification(); + note.expiry = Math.floor(Date.now() / 1e3) + 3_600; // Expires 1 hour from now. + note.alert = `New balance Ƚ ${slowWallet[0].toLocaleString('en-US')} on version ${BigInt(version).toLocaleString('en-US')}`; + note.payload = { + messageFrom: 'John Appleseed', + }; + note.topic = 'app.postero.postero'; + + const res = await this.apnProvider.send(note, subscription.device.token); + console.log('res', res); } - } break; + break; + + case DeviceType.ANDROID: + { + const { app } = this.firebaseService; + + if (app) { + const res = await app.messaging().send({ + token: subscription.device.token, + notification: { + title: `New balance Ƚ ${slowWallet[0].toLocaleString('en-US')}`, + body: `Version ${BigInt(version).toLocaleString('en-US')}`, + }, + data: { + story_id: 'story_12345', + }, + }); + console.log('res', res); + } + } + break; } } } @@ -174,15 +167,13 @@ export class WalletSubscriptionService { ) `, query_params: { version }, - format: "JSONCompact", + format: 'JSONCompact', }); const rows = await result.json<[number, string]>(); const slowWallets = rows.data; - const addresses = slowWallets.map(([_, address]) => - Buffer.from(address, "hex"), - ); + const addresses = slowWallets.map(([_, address]) => Buffer.from(address, 'hex')); const subscriptions = await this.prisma.walletSubscription.findMany({ where: { @@ -196,45 +187,44 @@ export class WalletSubscriptionService { }); for (const subscription of subscriptions) { - const walletAddress = subscription.walletAddress - .toString("hex") - .toUpperCase(); + const walletAddress = subscription.walletAddress.toString('hex').toUpperCase(); const slowWallet = slowWallets.find((it: any) => it[1] === walletAddress)!; switch (subscription.device.type) { - case DeviceType.IOS: { - const note = new apn.Notification(); - note.expiry = Math.floor(Date.now() / 1e3) + 3_600; // Expires 1 hour from now. - note.alert = `New unlocked amount Ƚ ${slowWallet[0].toLocaleString("en-US")} on version ${BigInt(version).toLocaleString("en-US")}`; - note.payload = { - messageFrom: "John Appleseed", - }; - note.topic = "app.postero.postero"; - - const res = await this.apnProvider.send( - note, - subscription.device.token, - ); - console.log("res", res); - } break; - - case DeviceType.ANDROID: { - const { app } = this.firebaseService; - - if (app) { - const res = await app.messaging().send({ - token: subscription.device.token, - notification: { - title: `New unlocked amount Ƚ ${slowWallet[0].toLocaleString("en-US")}`, - body: `Version ${BigInt(version).toLocaleString("en-US")}`, - }, - data: { - story_id: "story_12345", - }, - }); - console.log("res", res); + case DeviceType.IOS: + { + const note = new apn.Notification(); + note.expiry = Math.floor(Date.now() / 1e3) + 3_600; // Expires 1 hour from now. + note.alert = `New unlocked amount Ƚ ${slowWallet[0].toLocaleString('en-US')} on version ${BigInt(version).toLocaleString('en-US')}`; + note.payload = { + messageFrom: 'John Appleseed', + }; + note.topic = 'app.postero.postero'; + + const res = await this.apnProvider.send(note, subscription.device.token); + console.log('res', res); + } + break; + + case DeviceType.ANDROID: + { + const { app } = this.firebaseService; + + if (app) { + const res = await app.messaging().send({ + token: subscription.device.token, + notification: { + title: `New unlocked amount Ƚ ${slowWallet[0].toLocaleString('en-US')}`, + body: `Version ${BigInt(version).toLocaleString('en-US')}`, + }, + data: { + story_id: 'story_12345', + }, + }); + console.log('res', res); + } } - } break; + break; } } } diff --git a/api/test/app.e2e-spec.ts b/api/test/app.e2e-spec.ts index 3ec3061..9522b04 100644 --- a/api/test/app.e2e-spec.ts +++ b/api/test/app.e2e-spec.ts @@ -16,9 +16,6 @@ describe('AppController (e2e)', () => { }); it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); + return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!'); }); }); diff --git a/api/tsconfig.json b/api/tsconfig.json index 205e151..b91f7ed 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -18,4 +18,4 @@ "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false } -} \ No newline at end of file +}