diff --git a/blockchain-watcher/.github/pull-request-template.md b/blockchain-watcher/.github/pull-request-template.md new file mode 100644 index 00000000..2b276935 --- /dev/null +++ b/blockchain-watcher/.github/pull-request-template.md @@ -0,0 +1,11 @@ +## Link Issue + +<-- Link issue !--> + +## Description + +<-- Description of issue !--> + +## Test or Screensshots + +<-- How test this change !--> diff --git a/blockchain-watcher/jest.config.js b/blockchain-watcher/jest.config.js index 0dc2dbaf..0397fe2a 100644 --- a/blockchain-watcher/jest.config.js +++ b/blockchain-watcher/jest.config.js @@ -1,15 +1,18 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { - preset: "ts-jest", - testEnvironment: "node", - collectCoverageFrom: [ - "./src/domain", - "./src/infrastructure/mappers", - "./src/infrastructure/repositories", - ], + moduleFileExtensions: ["js", "json", "ts"], + roots: ["test", "src"], + testRegex: ".*\\.test\\.ts$", + transform: { + "^.+\\.(t|j)s$": "ts-jest", + }, + collectCoverage: true, + collectCoverageFrom: ["**/*.(t|j)s"], + coveragePathIgnorePatterns: ["node_modules", "test"], + coverageDirectory: "./coverage", coverageThreshold: { global: { - lines: 85, + lines: 55, }, }, }; diff --git a/blockchain-watcher/package.json b/blockchain-watcher/package.json index efdb6d80..77cfe3f8 100644 --- a/blockchain-watcher/package.json +++ b/blockchain-watcher/package.json @@ -9,6 +9,7 @@ "test:coverage": "jest --collectCoverage=true", "build": "tsc", "dev": "ts-node src/start.ts", + "dev:debug": "ts-node src/start.ts start --debug --watch", "prettier": "prettier --write ." }, "author": "chase-45", diff --git a/blockchain-watcher/src/domain/entities/index.ts b/blockchain-watcher/src/domain/entities/index.ts index e62d2ba7..05c8a96a 100644 --- a/blockchain-watcher/src/domain/entities/index.ts +++ b/blockchain-watcher/src/domain/entities/index.ts @@ -2,39 +2,3 @@ export * from "./evm"; export * from "./events"; export * from "./jobs"; export * as solana from "./solana"; - -export class Fallible { - private error?: E; - private value?: R; - - constructor(value?: R, error?: E) { - this.value = value; - this.error = error; - } - - public getValue(): R { - if (!this.value) { - throw this.error ? this.error : new Error("No value and no error present"); - } - return this.value; - } - - public getError(): E { - if (!this.error) { - throw new Error("No error present"); - } - return this.error; - } - - public isOk(): boolean { - return !this.error && this.value !== undefined; - } - - public static ok(value: R): Fallible { - return new Fallible(value); - } - - public static error(error: E): Fallible { - return new Fallible(undefined, error); - } -} diff --git a/blockchain-watcher/src/domain/entities/solana.ts b/blockchain-watcher/src/domain/entities/solana.ts index 28a1ca15..0a2b4df4 100644 --- a/blockchain-watcher/src/domain/entities/solana.ts +++ b/blockchain-watcher/src/domain/entities/solana.ts @@ -59,28 +59,3 @@ export enum ErrorType { SkippedSlot, NoBlockOrBlockTime, } - -export class Failure extends Error { - readonly code?: number | unknown; - readonly type?: ErrorType; - constructor(code: number | unknown, message: string, type?: ErrorType) { - super(message); - this.code = code; - - if (this.code === -32007 || this.code === -32009) { - this.type = ErrorType.SkippedSlot; - } - - if (type) { - this.type = type; - } - } - - public skippedSlot(): boolean { - return this.type === ErrorType.SkippedSlot; - } - - public noBlockOrBlockTime(): boolean { - return this.type === ErrorType.NoBlockOrBlockTime; - } -} diff --git a/blockchain-watcher/src/domain/errors/Fallible.ts b/blockchain-watcher/src/domain/errors/Fallible.ts new file mode 100644 index 00000000..f91d1444 --- /dev/null +++ b/blockchain-watcher/src/domain/errors/Fallible.ts @@ -0,0 +1,35 @@ +export class Fallible { + private value?: R; + private error?: E; + + constructor(value?: R, error?: E) { + this.value = value; + this.error = error; + } + + public getValue(): R { + if (!this.value) { + throw this.error ? this.error : new Error("No value and no error present"); + } + return this.value; + } + + public getError(): E { + if (!this.error) { + throw new Error("No error present"); + } + return this.error; + } + + public isOk(): boolean { + return !this.error && this.value !== undefined; + } + + public static ok(value: R): Fallible { + return new Fallible(value); + } + + public static error(error: E): Fallible { + return new Fallible(undefined, error); + } +} diff --git a/blockchain-watcher/src/domain/errors/SolanaFailure.ts b/blockchain-watcher/src/domain/errors/SolanaFailure.ts new file mode 100644 index 00000000..91899518 --- /dev/null +++ b/blockchain-watcher/src/domain/errors/SolanaFailure.ts @@ -0,0 +1,30 @@ +export enum ErrorType { + SkippedSlot, + NoBlockOrBlockTime, +} + +export class SolanaFailure extends Error { + readonly code?: number | unknown; + readonly type?: ErrorType; + + constructor(code: number | unknown, message: string, type?: ErrorType) { + super(message); + this.code = code; + + if (this.code === -32007 || this.code === -32009) { + this.type = ErrorType.SkippedSlot; + } + + if (type) { + this.type = type; + } + } + + public skippedSlot(): boolean { + return this.type === ErrorType.SkippedSlot; + } + + public noBlockOrBlockTime(): boolean { + return this.type === ErrorType.NoBlockOrBlockTime; + } +} diff --git a/blockchain-watcher/src/domain/errors/index.ts b/blockchain-watcher/src/domain/errors/index.ts new file mode 100644 index 00000000..9dcd71cf --- /dev/null +++ b/blockchain-watcher/src/domain/errors/index.ts @@ -0,0 +1,2 @@ +export * from "./SolanaFailure"; +export * from "./Fallible"; diff --git a/blockchain-watcher/src/domain/repositories.ts b/blockchain-watcher/src/domain/repositories.ts index 8e6969d7..1dfb0546 100644 --- a/blockchain-watcher/src/domain/repositories.ts +++ b/blockchain-watcher/src/domain/repositories.ts @@ -1,14 +1,7 @@ import { RunPollingJob } from "./actions/RunPollingJob"; -import { - EvmBlock, - EvmLog, - EvmLogFilter, - Fallible, - Handler, - JobDefinition, - solana, -} from "./entities"; +import { EvmBlock, EvmLog, EvmLogFilter, Handler, JobDefinition, solana } from "./entities"; import { ConfirmedSignatureInfo } from "./entities/solana"; +import { Fallible, SolanaFailure } from "./errors"; export interface EvmBlockRepository { getBlockHeight(finality: string): Promise; @@ -18,7 +11,7 @@ export interface EvmBlockRepository { export interface SolanaSlotRepository { getLatestSlot(commitment: string): Promise; - getBlock(slot: number, finality?: string): Promise>; + getBlock(slot: number, finality?: string): Promise>; getSignaturesForAddress( address: string, beforeSig: string, diff --git a/blockchain-watcher/src/infrastructure/RepositoriesBuilder.ts b/blockchain-watcher/src/infrastructure/RepositoriesBuilder.ts index bf4a4257..96fb7d05 100644 --- a/blockchain-watcher/src/infrastructure/RepositoriesBuilder.ts +++ b/blockchain-watcher/src/infrastructure/RepositoriesBuilder.ts @@ -5,13 +5,13 @@ import { SnsEventRepository, EvmJsonRPCBlockRepository, EvmJsonRPCBlockRepositoryCfg, - FileMetadataRepo, + FileMetadataRepository, PromStatRepository, StaticJobRepository, Web3SolanaSlotRepository, } from "./repositories"; -import { HttpClient } from "./repositories/HttpClient"; +import { HttpClient } from "./http/HttpClient"; import { JobRepository } from "../domain/repositories"; export class RepositoriesBuilder { @@ -31,7 +31,7 @@ export class RepositoriesBuilder { this.repositories.set("metrics", new PromStatRepository()); this.cfg.metadata?.dir && - this.repositories.set("metadata", new FileMetadataRepo(this.cfg.metadata.dir)); + this.repositories.set("metadata", new FileMetadataRepository(this.cfg.metadata.dir)); this.cfg.supportedChains.forEach((chain) => { if (!this.cfg.platforms[chain]) throw new Error(`No config for chain ${chain}`); @@ -80,7 +80,7 @@ export class RepositoriesBuilder { return this.getRepo("sns"); } - public getMetadataRepository(): FileMetadataRepo { + public getMetadataRepository(): FileMetadataRepository { return this.getRepo("metadata"); } diff --git a/blockchain-watcher/src/infrastructure/repositories/HttpClient.ts b/blockchain-watcher/src/infrastructure/http/HttpClient.ts similarity index 100% rename from blockchain-watcher/src/infrastructure/repositories/HttpClient.ts rename to blockchain-watcher/src/infrastructure/http/HttpClient.ts diff --git a/blockchain-watcher/src/infrastructure/repositories/EvmJsonRPCBlockRepository.ts b/blockchain-watcher/src/infrastructure/repositories/EvmJsonRPCBlockRepository.ts index 8551f708..efdb261a 100644 --- a/blockchain-watcher/src/infrastructure/repositories/EvmJsonRPCBlockRepository.ts +++ b/blockchain-watcher/src/infrastructure/repositories/EvmJsonRPCBlockRepository.ts @@ -2,7 +2,7 @@ import { EvmBlock, EvmLogFilter, EvmLog, EvmTag } from "../../domain/entities"; import { EvmBlockRepository } from "../../domain/repositories"; import { AxiosInstance } from "axios"; import winston from "../log"; -import { HttpClient, HttpClientError } from "./HttpClient"; +import { HttpClient, HttpClientError } from "../http/HttpClient"; /** * EvmJsonRPCBlockRepository is a repository that uses a JSON RPC endpoint to fetch blocks. diff --git a/blockchain-watcher/src/infrastructure/repositories/FileMetadataRepo.ts b/blockchain-watcher/src/infrastructure/repositories/FileMetadataRepository.ts similarity index 90% rename from blockchain-watcher/src/infrastructure/repositories/FileMetadataRepo.ts rename to blockchain-watcher/src/infrastructure/repositories/FileMetadataRepository.ts index d4f2f248..b2f47e92 100644 --- a/blockchain-watcher/src/infrastructure/repositories/FileMetadataRepo.ts +++ b/blockchain-watcher/src/infrastructure/repositories/FileMetadataRepository.ts @@ -1,7 +1,7 @@ import fs from "fs"; import { MetadataRepository } from "../../domain/repositories"; -export class FileMetadataRepo implements MetadataRepository { +export class FileMetadataRepository implements MetadataRepository { private readonly dirPath: string; constructor(dirPath: string) { diff --git a/blockchain-watcher/src/infrastructure/repositories/StaticJobRepository.ts b/blockchain-watcher/src/infrastructure/repositories/StaticJobRepository.ts index 357f5cde..aebf86cf 100644 --- a/blockchain-watcher/src/infrastructure/repositories/StaticJobRepository.ts +++ b/blockchain-watcher/src/infrastructure/repositories/StaticJobRepository.ts @@ -15,13 +15,13 @@ import { SolanaSlotRepository, StatRepository, } from "../../domain/repositories"; -import { FileMetadataRepo, SnsEventRepository } from "./index"; +import { FileMetadataRepository, SnsEventRepository } from "./index"; import { HandleSolanaTransactions } from "../../domain/actions/solana/HandleSolanaTransactions"; import { solanaLogMessagePublishedMapper, evmLogMessagePublishedMapper } from "../mappers"; import log from "../log"; export class StaticJobRepository implements JobRepository { - private fileRepo: FileMetadataRepo; + private fileRepo: FileMetadataRepository; private dryRun: boolean = false; private sources: Map RunPollingJob> = new Map(); private handlers: Map Promise> = @@ -45,7 +45,7 @@ export class StaticJobRepository implements JobRepository { solanaSlotRepo: SolanaSlotRepository; } ) { - this.fileRepo = new FileMetadataRepo(path); + this.fileRepo = new FileMetadataRepository(path); this.blockRepoProvider = blockRepoProvider; this.metadataRepo = repos.metadataRepo; this.statsRepo = repos.statsRepo; diff --git a/blockchain-watcher/src/infrastructure/repositories/Web3SolanaSlotRepository.ts b/blockchain-watcher/src/infrastructure/repositories/Web3SolanaSlotRepository.ts index 43d8a066..51d0e4f4 100644 --- a/blockchain-watcher/src/infrastructure/repositories/Web3SolanaSlotRepository.ts +++ b/blockchain-watcher/src/infrastructure/repositories/Web3SolanaSlotRepository.ts @@ -4,11 +4,11 @@ import { PublicKey, VersionedTransactionResponse, SolanaJSONRPCError, - Finality, } from "@solana/web3.js"; -import { Fallible, solana } from "../../domain/entities"; +import { solana } from "../../domain/entities"; import { SolanaSlotRepository } from "../../domain/repositories"; +import { Fallible, SolanaFailure } from "../../domain/errors"; export class Web3SolanaSlotRepository implements SolanaSlotRepository { connection: Connection; @@ -21,7 +21,7 @@ export class Web3SolanaSlotRepository implements SolanaSlotRepository { return this.connection.getSlot(commitment as Commitment); } - getBlock(slot: number, finality?: string): Promise> { + getBlock(slot: number, finality?: string): Promise> { return this.connection .getBlock(slot, { maxSupportedTransactionVersion: 0, @@ -29,21 +29,21 @@ export class Web3SolanaSlotRepository implements SolanaSlotRepository { }) .then((block) => { if (block === null) { - return Fallible.error( - new solana.Failure(0, "Block not found") + return Fallible.error( + new SolanaFailure(0, "Block not found") ); } - return Fallible.ok({ + return Fallible.ok({ ...block, transactions: block.transactions.map((tx) => this.mapTx(tx, slot)), }); }) .catch((err) => { if (err instanceof SolanaJSONRPCError) { - return Fallible.error(new solana.Failure(err.code, err.message)); + return Fallible.error(new SolanaFailure(err.code, err.message)); } - return Fallible.error(new solana.Failure(0, err.message)); + return Fallible.error(new SolanaFailure(0, err.message)); }); } diff --git a/blockchain-watcher/src/infrastructure/repositories/index.ts b/blockchain-watcher/src/infrastructure/repositories/index.ts index fe150948..2ff0e5fb 100644 --- a/blockchain-watcher/src/infrastructure/repositories/index.ts +++ b/blockchain-watcher/src/infrastructure/repositories/index.ts @@ -7,7 +7,7 @@ if (!("toJSON" in BigInt.prototype)) { }); } -export * from "./FileMetadataRepo"; +export * from "./FileMetadataRepository"; export * from "./SnsEventRepository"; export * from "./EvmJsonRPCBlockRepository"; export * from "./PromStatRepository"; diff --git a/blockchain-watcher/test/domain/actions/solana/PollSolanaTransactions.test.ts b/blockchain-watcher/test/domain/actions/solana/PollSolanaTransactions.test.ts index 267dc7e6..e2fcb3c2 100644 --- a/blockchain-watcher/test/domain/actions/solana/PollSolanaTransactions.test.ts +++ b/blockchain-watcher/test/domain/actions/solana/PollSolanaTransactions.test.ts @@ -10,7 +10,8 @@ import { StatRepository, } from "../../../../src/domain/repositories"; import { thenWaitForAssertion } from "../../../wait-assertion"; -import { Fallible, solana } from "../../../../src/domain/entities"; +import { solana } from "../../../../src/domain/entities"; +import { Fallible } from "../../../../src/domain/errors"; let pollSolanaTransactions: PollSolanaTransactions; let cfg: PollSolanaTransactionsConfig; diff --git a/blockchain-watcher/test/infrastructure/repositories/EvmJsonRPCBlockRepository.integration.test.ts b/blockchain-watcher/test/infrastructure/repositories/EvmJsonRPCBlockRepository.integration.test.ts index ddaff3a2..9a8b478b 100644 --- a/blockchain-watcher/test/infrastructure/repositories/EvmJsonRPCBlockRepository.integration.test.ts +++ b/blockchain-watcher/test/infrastructure/repositories/EvmJsonRPCBlockRepository.integration.test.ts @@ -3,7 +3,7 @@ import { EvmJsonRPCBlockRepository } from "../../../src/infrastructure/repositor import axios from "axios"; import nock from "nock"; import { EvmLogFilter, EvmTag } from "../../../src/domain/entities"; -import { HttpClient } from "../../../src/infrastructure/repositories/HttpClient"; +import { HttpClient } from "../../../src/infrastructure/http/HttpClient"; axios.defaults.adapter = "http"; // needed by nock const rpc = "http://localhost"; diff --git a/blockchain-watcher/test/infrastructure/repositories/FileMetadataRepo.test.ts b/blockchain-watcher/test/infrastructure/repositories/FileMetadataRepo.test.ts index 77d0e9db..c5f8ab5b 100644 --- a/blockchain-watcher/test/infrastructure/repositories/FileMetadataRepo.test.ts +++ b/blockchain-watcher/test/infrastructure/repositories/FileMetadataRepo.test.ts @@ -1,10 +1,10 @@ import { describe, expect, it, beforeEach, afterEach } from "@jest/globals"; import fs from "fs"; -import { FileMetadataRepo } from "../../../src/infrastructure/repositories"; +import { FileMetadataRepository } from "../../../src/infrastructure/repositories"; -describe("FileMetadataRepo", () => { +describe("FileMetadataRepository", () => { const dirPath = "./metadata-repo"; - const repo = new FileMetadataRepo(dirPath); + const repo = new FileMetadataRepository(dirPath); beforeEach(() => { if (!fs.existsSync(dirPath)) {