diff --git a/apps/agent/src/index.ts b/apps/agent/src/index.ts index 0f75f945..9124bc67 100644 --- a/apps/agent/src/index.ts +++ b/apps/agent/src/index.ts @@ -3,12 +3,11 @@ import { isNativeError } from "util/types"; import { EboActorsManager, EboProcessor, - NotificationService, NotificationServiceFactory, ProtocolProvider, } from "@ebo-agent/automated-dispute"; import { BlockNumberService } from "@ebo-agent/blocknumber"; -import { Logger, stringify } from "@ebo-agent/shared"; +import { Logger, NotificationService, stringify } from "@ebo-agent/shared"; import { config } from "./config/index.js"; @@ -17,13 +16,21 @@ const logger = Logger.getInstance(); const main = async (): Promise => { logger.debug("Initializing agent..."); + logger.debug("Initializing notification service..."); + + const notifier: NotificationService = NotificationServiceFactory.create(logger); + + logger.debug("Initialized notification service."); + logger.debug("Initializing block number service..."); + logger.debug(stringify(config.blockNumberService)); const blockNumberService = new BlockNumberService( config.blockNumberService.chainRpcUrls, config.blockNumberService.blockmetaConfig, logger, + notifier, ); logger.debug("Block number service initialized."); @@ -41,12 +48,6 @@ const main = async (): Promise => { logger.debug("Protocol provider initialized."); - logger.debug("Initializing notification service..."); - - const notifier: NotificationService = NotificationServiceFactory.create(logger); - - logger.debug("Initialized notification service."); - const actorsManager = new EboActorsManager(); logger.debug("Initializing EBO processor..."); diff --git a/packages/automated-dispute/src/exceptions/errorHandler.ts b/packages/automated-dispute/src/exceptions/errorHandler.ts index 1ca073d4..1ee0731e 100644 --- a/packages/automated-dispute/src/exceptions/errorHandler.ts +++ b/packages/automated-dispute/src/exceptions/errorHandler.ts @@ -1,7 +1,6 @@ -import { ILogger } from "@ebo-agent/shared"; +import { ILogger, NotificationService } from "@ebo-agent/shared"; import { CustomContractError } from "../exceptions/index.js"; -import { NotificationService } from "../interfaces/index.js"; export class ErrorHandler { private notificationService: NotificationService; diff --git a/packages/automated-dispute/src/external.ts b/packages/automated-dispute/src/external.ts index f63d7725..d54edc73 100644 --- a/packages/automated-dispute/src/external.ts +++ b/packages/automated-dispute/src/external.ts @@ -1,5 +1,4 @@ export { EboProcessor, EboActorsManager, DiscordNotifier, ProphetCodec } from "./services/index.js"; -export type { NotificationService } from "./interfaces/index.js"; export { NullNotificationService, NotificationServiceFactory } from "./interfaces/index.js"; export { ProtocolProvider } from "./providers/index.js"; export type { AccountingModules } from "./types/index.js"; diff --git a/packages/automated-dispute/src/interfaces/index.ts b/packages/automated-dispute/src/interfaces/index.ts index 60b9629e..91f7e47e 100644 --- a/packages/automated-dispute/src/interfaces/index.ts +++ b/packages/automated-dispute/src/interfaces/index.ts @@ -1,6 +1,5 @@ export * from "./eboRegistry.js"; export * from "./eboRegistryCommand.js"; export * from "./protocolProvider.js"; -export * from "./notificationService.js"; export * from "./nullNotificationService.js"; export * from "./notificationServiceFactory.js"; diff --git a/packages/automated-dispute/src/interfaces/notificationServiceFactory.ts b/packages/automated-dispute/src/interfaces/notificationServiceFactory.ts index 57f182fb..883923bb 100644 --- a/packages/automated-dispute/src/interfaces/notificationServiceFactory.ts +++ b/packages/automated-dispute/src/interfaces/notificationServiceFactory.ts @@ -1,8 +1,7 @@ -import { ILogger } from "@ebo-agent/shared"; +import { ILogger, NotificationService } from "@ebo-agent/shared"; import { config } from "../config.js"; import { DiscordNotifier } from "../services/index.js"; -import { NotificationService } from "./notificationService.js"; import { NullNotificationService } from "./nullNotificationService.js"; /** diff --git a/packages/automated-dispute/src/interfaces/nullNotificationService.ts b/packages/automated-dispute/src/interfaces/nullNotificationService.ts index 13d9d679..04290733 100644 --- a/packages/automated-dispute/src/interfaces/nullNotificationService.ts +++ b/packages/automated-dispute/src/interfaces/nullNotificationService.ts @@ -1,4 +1,4 @@ -import { IMessage, NotificationService } from "./notificationService.js"; +import { IMessage, NotificationService } from "@ebo-agent/shared"; /** * A null implementation of NotificationService that performs no actions but allows the application to have an undefined notificationService if desired diff --git a/packages/automated-dispute/src/services/discordNotifier.ts b/packages/automated-dispute/src/services/discordNotifier.ts index 9872e342..3e6c0001 100644 --- a/packages/automated-dispute/src/services/discordNotifier.ts +++ b/packages/automated-dispute/src/services/discordNotifier.ts @@ -1,10 +1,9 @@ import { isNativeError } from "util/types"; import type { APIEmbed, JSONEncodable } from "discord.js"; -import { ILogger, stringify } from "@ebo-agent/shared"; +import { ILogger, IMessage, NotificationService, stringify } from "@ebo-agent/shared"; import { WebhookClient, WebhookMessageCreateOptions } from "discord.js"; import { NotificationFailureException } from "../exceptions/index.js"; -import { IMessage, NotificationService } from "../interfaces/index.js"; export type WebhookMessage = WebhookMessageCreateOptions & { content: string; diff --git a/packages/automated-dispute/src/services/eboActor.ts b/packages/automated-dispute/src/services/eboActor.ts index b883355f..fc235245 100644 --- a/packages/automated-dispute/src/services/eboActor.ts +++ b/packages/automated-dispute/src/services/eboActor.ts @@ -1,6 +1,12 @@ import { isNativeError } from "util/types"; import { BlockNumberService } from "@ebo-agent/blocknumber"; -import { Caip2ChainId, ILogger, stringify, UnixTimestamp } from "@ebo-agent/shared"; +import { + Caip2ChainId, + ILogger, + NotificationService, + stringify, + UnixTimestamp, +} from "@ebo-agent/shared"; import { Mutex } from "async-mutex"; import { Heap } from "heap-js"; import { ContractFunctionRevertedError } from "viem"; @@ -30,7 +36,7 @@ import { ResponseNotFound, UnknownEvent, } from "../exceptions/index.js"; -import { EboRegistry, EboRegistryCommand, NotificationService } from "../interfaces/index.js"; +import { EboRegistry, EboRegistryCommand } from "../interfaces/index.js"; import { ProtocolProvider } from "../providers/index.js"; import { AddDispute, diff --git a/packages/automated-dispute/src/services/eboActorsManager.ts b/packages/automated-dispute/src/services/eboActorsManager.ts index 71cc2307..6f2e990b 100644 --- a/packages/automated-dispute/src/services/eboActorsManager.ts +++ b/packages/automated-dispute/src/services/eboActorsManager.ts @@ -1,9 +1,8 @@ import { BlockNumberService } from "@ebo-agent/blocknumber"; -import { ILogger } from "@ebo-agent/shared"; +import { ILogger, NotificationService } from "@ebo-agent/shared"; import { Mutex } from "async-mutex"; import { RequestAlreadyHandled } from "../exceptions/index.js"; -import { NotificationService } from "../interfaces/index.js"; import { ProtocolProvider } from "../providers/protocolProvider.js"; import { ActorRequest, RequestId } from "../types/index.js"; import { EboActor } from "./eboActor.js"; diff --git a/packages/automated-dispute/src/services/eboProcessor.ts b/packages/automated-dispute/src/services/eboProcessor.ts index 428b1a88..966aac68 100644 --- a/packages/automated-dispute/src/services/eboProcessor.ts +++ b/packages/automated-dispute/src/services/eboProcessor.ts @@ -1,6 +1,13 @@ import { isNativeError } from "util/types"; import { BlockNumberService } from "@ebo-agent/blocknumber"; -import { Caip2ChainId, Caip2Utils, ILogger, stringify, UnixTimestamp } from "@ebo-agent/shared"; +import { + Caip2ChainId, + Caip2Utils, + ILogger, + NotificationService, + stringify, + UnixTimestamp, +} from "@ebo-agent/shared"; import { Address, Block, ContractFunctionRevertedError } from "viem"; import { @@ -9,7 +16,6 @@ import { ProcessorAlreadyStarted, } from "../exceptions/index.js"; import { isRequestCreatedEvent } from "../guards.js"; -import { NotificationService } from "../interfaces/index.js"; import { ProtocolProvider } from "../providers/index.js"; import { alreadyDeletedActorWarning, diff --git a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts index 7d3bde41..262d9463 100644 --- a/packages/automated-dispute/tests/mocks/eboActor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboActor.mocks.ts @@ -1,10 +1,9 @@ import { BlockNumberService } from "@ebo-agent/blocknumber"; -import { Caip2ChainId, ILogger, UnixTimestamp } from "@ebo-agent/shared"; +import { Caip2ChainId, ILogger, NotificationService, UnixTimestamp } from "@ebo-agent/shared"; import { Mutex } from "async-mutex"; import { Block, pad } from "viem"; import { vi } from "vitest"; -import { NotificationService } from "../../src/index.js"; import { ProtocolProvider } from "../../src/providers/index.js"; import { EboActor, EboMemoryRegistry, ProphetCodec } from "../../src/services/index.js"; import { diff --git a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts index 464bebfd..eb2b1832 100644 --- a/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts +++ b/packages/automated-dispute/tests/mocks/eboProcessor.mocks.ts @@ -1,8 +1,7 @@ import { BlockNumberService } from "@ebo-agent/blocknumber"; -import { Caip2ChainId, ILogger } from "@ebo-agent/shared"; +import { Caip2ChainId, ILogger, NotificationService } from "@ebo-agent/shared"; import { vi } from "vitest"; -import { NotificationService } from "../../src/index.js"; import { ProtocolProvider } from "../../src/providers/index.js"; import { EboActorsManager, EboProcessor } from "../../src/services/index.js"; import { AccountingModules } from "../../src/types/prophet.js"; @@ -35,6 +34,7 @@ export function buildEboProcessor( }, }, logger, + notifier, ); const protocolProvider = new ProtocolProvider( diff --git a/packages/automated-dispute/tests/services/discordNotifier.spec.ts b/packages/automated-dispute/tests/services/discordNotifier.spec.ts index 41ed90c6..667b2339 100644 --- a/packages/automated-dispute/tests/services/discordNotifier.spec.ts +++ b/packages/automated-dispute/tests/services/discordNotifier.spec.ts @@ -1,10 +1,9 @@ -import { ILogger, stringify } from "@ebo-agent/shared"; +import { ILogger, IMessage, stringify } from "@ebo-agent/shared"; import { WebhookClient } from "discord.js"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { NotificationFailureException } from "../../src/exceptions/index.js"; import { DiscordNotifier } from "../../src/index.js"; -import { IMessage } from "../../src/interfaces/index.js"; vi.mock("discord.js", () => { const mockSend = vi.fn(); diff --git a/packages/automated-dispute/tests/services/eboProcessor.spec.ts b/packages/automated-dispute/tests/services/eboProcessor.spec.ts index 8d0baedb..2918e939 100644 --- a/packages/automated-dispute/tests/services/eboProcessor.spec.ts +++ b/packages/automated-dispute/tests/services/eboProcessor.spec.ts @@ -1,4 +1,4 @@ -import { Caip2ChainId, UnixTimestamp } from "@ebo-agent/shared"; +import { Caip2ChainId, NotificationService, UnixTimestamp } from "@ebo-agent/shared"; import { Caip2Utils } from "@ebo-agent/shared/src/index.js"; import { Block, Hex } from "viem"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; @@ -8,7 +8,7 @@ import { PendingModulesApproval, ProcessorAlreadyStarted, } from "../../src/exceptions/index.js"; -import { NotificationService, ProphetCodec } from "../../src/index.js"; +import { ProphetCodec } from "../../src/index.js"; import { AccountingModules, EboEvent, diff --git a/packages/automated-dispute/tests/services/notificationServiceFactory.test.ts b/packages/automated-dispute/tests/services/notificationServiceFactory.test.ts index 3130b184..7c4860b8 100644 --- a/packages/automated-dispute/tests/services/notificationServiceFactory.test.ts +++ b/packages/automated-dispute/tests/services/notificationServiceFactory.test.ts @@ -1,8 +1,6 @@ -import { ILogger } from "@ebo-agent/shared"; +import { ILogger, NotificationService } from "@ebo-agent/shared"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { NotificationService } from "../../src/index.js"; - vi.mock("../../src/services/discordNotifier.js", () => ({ DiscordNotifier: vi.fn(), })); diff --git a/packages/automated-dispute/tests/services/nullNotificationService.test.ts b/packages/automated-dispute/tests/services/nullNotificationService.test.ts index ac4f5f87..75bfdb2a 100644 --- a/packages/automated-dispute/tests/services/nullNotificationService.test.ts +++ b/packages/automated-dispute/tests/services/nullNotificationService.test.ts @@ -1,7 +1,7 @@ +import { IMessage, NotificationService } from "@ebo-agent/shared"; import { beforeEach, describe, expect, it } from "vitest"; -import { NotificationService } from "../../src/index.js"; -import { IMessage, NullNotificationService } from "../../src/interfaces/index.js"; +import { NullNotificationService } from "../../src/interfaces/index.js"; describe("NullNotificationService", () => { let notifier: NotificationService; diff --git a/packages/blocknumber/src/providers/blockNumberProviderFactory.ts b/packages/blocknumber/src/providers/blockNumberProviderFactory.ts index 32254861..5250bab8 100644 --- a/packages/blocknumber/src/providers/blockNumberProviderFactory.ts +++ b/packages/blocknumber/src/providers/blockNumberProviderFactory.ts @@ -1,4 +1,10 @@ -import { Caip2ChainId, Caip2Utils, EBO_SUPPORTED_CHAINS_CONFIG, ILogger } from "@ebo-agent/shared"; +import { + Caip2ChainId, + Caip2Utils, + EBO_SUPPORTED_CHAINS_CONFIG, + ILogger, + NotificationService, +} from "@ebo-agent/shared"; import { FallbackTransport, HttpTransport, PublicClient } from "viem"; import { UnsupportedChain } from "../exceptions/unsupportedChain.js"; @@ -19,7 +25,9 @@ export class BlockNumberProviderFactory { * * @param chainId CAIP-2 chain id * @param evmClient a viem public client + * @param blockmetaConfig a BlockmetaClientConfig instance * @param logger a ILogger instance + * @param notificationService a NotificationService instance * @returns */ public static buildProvider( @@ -27,6 +35,7 @@ export class BlockNumberProviderFactory { evmClient: PublicClient>, blockmetaConfig: BlockmetaClientConfig, logger: ILogger, + notificationService: NotificationService, ) { // TODO: initialize factory instance with evmClient and blockmetaConfig and // remove them from this method parameters @@ -37,7 +46,11 @@ export class BlockNumberProviderFactory { return new EvmBlockNumberProvider(evmClient, DEFAULT_PROVIDER_CONFIG, logger); case EBO_SUPPORTED_CHAINS_CONFIG.solana.namespace: - return new BlockmetaJsonBlockNumberProvider(blockmetaConfig, logger); + return new BlockmetaJsonBlockNumberProvider( + blockmetaConfig, + logger, + notificationService, + ); default: throw new UnsupportedChain(chainId); diff --git a/packages/blocknumber/src/providers/blockmetaJsonBlockNumberProvider.ts b/packages/blocknumber/src/providers/blockmetaJsonBlockNumberProvider.ts index 8434c8ed..02106d4a 100644 --- a/packages/blocknumber/src/providers/blockmetaJsonBlockNumberProvider.ts +++ b/packages/blocknumber/src/providers/blockmetaJsonBlockNumberProvider.ts @@ -1,4 +1,4 @@ -import { ILogger, UnixTimestamp } from "@ebo-agent/shared"; +import { ILogger, NotificationService, UnixTimestamp } from "@ebo-agent/shared"; import axios, { AxiosInstance, AxiosResponse, @@ -41,6 +41,7 @@ export class BlockmetaJsonBlockNumberProvider implements BlockNumberProvider { constructor( private readonly clientConfig: BlockmetaClientConfig, private readonly logger: ILogger, + private readonly notificationService: NotificationService, ) { const { baseUrl, bearerToken } = clientConfig; @@ -62,8 +63,9 @@ export class BlockmetaJsonBlockNumberProvider implements BlockNumberProvider { public static async initialize( config: BlockmetaClientConfig, logger: ILogger, + notificationService: NotificationService, ): Promise { - const provider = new BlockmetaJsonBlockNumberProvider(config, logger); + const provider = new BlockmetaJsonBlockNumberProvider(config, logger, notificationService); const connectedSuccessfully = await provider.testConnection(); @@ -108,9 +110,17 @@ export class BlockmetaJsonBlockNumberProvider implements BlockNumberProvider { if (currentTime + expirationWindow >= expTime) { const timeRemaining = expTime - currentTime; - this.logger.warn(`Token will expire soon in ${timeRemaining}`); - - // TODO: notify + this.logger.warn(`Token will expire soon in ${timeRemaining} seconds.`); + + this.notificationService + .sendError( + "Token Expiration Warning", + { timeRemaining }, + null, // No error obj for warnings + ) + .catch((err: unknown) => { + this.logger.error(`Failed to send expiration warning notification: ${err}`); + }); } return requestConfig; @@ -118,7 +128,13 @@ export class BlockmetaJsonBlockNumberProvider implements BlockNumberProvider { if (err instanceof InvalidTokenError) { this.logger.error("Invalid JWT token."); - // TODO: notify + this.notificationService + .sendError("Invalid JWT Token Error", {}, err) + .catch((notificationError: unknown) => { + this.logger.error( + `Failed to send invalid token notification: ${notificationError}`, + ); + }); } return requestConfig; diff --git a/packages/blocknumber/src/services/blockNumberService.ts b/packages/blocknumber/src/services/blockNumberService.ts index 75d5b3b9..1d9baa25 100644 --- a/packages/blocknumber/src/services/blockNumberService.ts +++ b/packages/blocknumber/src/services/blockNumberService.ts @@ -1,4 +1,10 @@ -import { Caip2ChainId, EBO_SUPPORTED_CHAIN_IDS, ILogger, UnixTimestamp } from "@ebo-agent/shared"; +import { + Caip2ChainId, + EBO_SUPPORTED_CHAIN_IDS, + ILogger, + NotificationService, + UnixTimestamp, +} from "@ebo-agent/shared"; import { createPublicClient, fallback, http } from "viem"; import { ChainWithoutProvider, EmptyRpcUrls, UnsupportedChain } from "../exceptions/index.js"; @@ -16,12 +22,15 @@ export class BlockNumberService { * of chains. * * @param chainRpcUrls a map of CAIP-2 chain ids with their RPC urls that this service will handle + * @param blockmetaConfig a `BlockmetaClientConfig` instance * @param logger a `ILogger` instance + * @param notificationService a `NotificationService` instance */ constructor( chainRpcUrls: Map, private readonly blockmetaConfig: BlockmetaClientConfig, private readonly logger: ILogger, + private readonly notificationService: NotificationService, ) { this.blockNumberProviders = this.buildBlockNumberProviders(chainRpcUrls); } @@ -90,6 +99,7 @@ export class BlockNumberService { client, this.blockmetaConfig, this.logger, + this.notificationService, ); if (!provider) throw new ChainWithoutProvider(chainId); diff --git a/packages/blocknumber/test/providers/blockNumberProviderFactory.spec.ts b/packages/blocknumber/test/providers/blockNumberProviderFactory.spec.ts index 33092f02..b6121d2c 100644 --- a/packages/blocknumber/test/providers/blockNumberProviderFactory.spec.ts +++ b/packages/blocknumber/test/providers/blockNumberProviderFactory.spec.ts @@ -1,4 +1,4 @@ -import { Logger } from "@ebo-agent/shared"; +import { Caip2ChainId, Logger, NotificationService } from "@ebo-agent/shared"; import { createPublicClient, fallback, @@ -7,7 +7,7 @@ import { HttpTransport, PublicClient, } from "viem"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { UnsupportedChain } from "../../src/exceptions/index.js"; import { @@ -16,7 +16,6 @@ import { BlockNumberProviderFactory, EvmBlockNumberProvider, } from "../../src/providers/index.js"; -import { Caip2ChainId } from "../../src/types.js"; describe("BlockNumberProviderFactory", () => { const logger = Logger.getInstance(); @@ -26,9 +25,20 @@ describe("BlockNumberProviderFactory", () => { }); const blockmetaConfig: BlockmetaClientConfig = { - baseUrl: new URL("localhost:443"), - servicePath: "/sf.blockmeta.v2.BlockByTime", + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; + + const mockNotificationService: NotificationService = { + send: vi.fn().mockResolvedValue(undefined), + sendOrThrow: vi.fn().mockResolvedValue(undefined), + createErrorMessage: vi.fn().mockReturnValue({ title: "Test", description: "Test" }), + sendError: vi.fn().mockResolvedValue(undefined), }; describe("buildProvider", () => { @@ -38,17 +48,19 @@ describe("BlockNumberProviderFactory", () => { client, blockmetaConfig, logger, + mockNotificationService, ); expect(provider).toBeInstanceOf(EvmBlockNumberProvider); }); - it("builds a Solana Blockmeta provider", () => { - const provider = BlockNumberProviderFactory.buildProvider( + it("builds a Solana Blockmeta provider", async () => { + const provider = await BlockNumberProviderFactory.buildProvider( "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", client, blockmetaConfig, logger, + mockNotificationService, ); expect(provider).toBeInstanceOf(BlockmetaJsonBlockNumberProvider); @@ -63,6 +75,7 @@ describe("BlockNumberProviderFactory", () => { client, blockmetaConfig, logger, + mockNotificationService, ); }).toThrow(UnsupportedChain); }); diff --git a/packages/blocknumber/test/services/blockNumberService.spec.ts b/packages/blocknumber/test/services/blockNumberService.spec.ts index 4894b0b8..557baa47 100644 --- a/packages/blocknumber/test/services/blockNumberService.spec.ts +++ b/packages/blocknumber/test/services/blockNumberService.spec.ts @@ -1,11 +1,10 @@ -import { Logger } from "@ebo-agent/shared"; +import { Caip2ChainId, Logger, NotificationService, UnixTimestamp } from "@ebo-agent/shared"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ChainWithoutProvider, EmptyRpcUrls } from "../../src/exceptions/index.js"; import { BlockNumberProviderFactory } from "../../src/providers/blockNumberProviderFactory.js"; import { EvmBlockNumberProvider } from "../../src/providers/evmBlockNumberProvider.js"; import { BlockNumberService } from "../../src/services/index.js"; -import { Caip2ChainId } from "../../src/types.js"; describe("BlockNumberService", () => { const logger = Logger.getInstance(); @@ -30,6 +29,13 @@ describe("BlockNumberService", () => { ]), ); + const mockNotificationService: NotificationService = { + send: vi.fn().mockResolvedValue(undefined), + sendOrThrow: vi.fn().mockResolvedValue(undefined), + createErrorMessage: vi.fn().mockReturnValue({ title: "Test", description: "Test" }), + sendError: vi.fn().mockResolvedValue(undefined), + }; + beforeEach(() => { spyWithDummyProviders(dummyProviders); }); @@ -39,15 +45,47 @@ describe("BlockNumberService", () => { }); it("creates an instance of BlockNumberService", () => { - const service = new BlockNumberService(rpcUrls, logger); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; + + const service = new BlockNumberService( + rpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ); expect(service).toBeInstanceOf(BlockNumberService); }); it("fails if initialized without any chain", () => { const emptyRpcUrls = new Map(); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; - expect(() => new BlockNumberService(emptyRpcUrls, logger)).toThrow(EmptyRpcUrls); + expect( + () => + new BlockNumberService( + emptyRpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ), + ).toThrow(EmptyRpcUrls); }); }); @@ -71,6 +109,13 @@ describe("BlockNumberService", () => { ]), ); + const mockNotificationService: NotificationService = { + send: vi.fn().mockResolvedValue(undefined), + sendOrThrow: vi.fn().mockResolvedValue(undefined), + createErrorMessage: vi.fn().mockReturnValue({ title: "Test", description: "Test" }), + sendError: vi.fn().mockResolvedValue(undefined), + }; + beforeEach(() => { spyWithDummyProviders(dummyProviders); }); @@ -80,9 +125,24 @@ describe("BlockNumberService", () => { }); it("returns the chain epoch block number", async () => { - const service = new BlockNumberService(rpcUrls, logger); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; - const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const service = new BlockNumberService( + rpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ); + + const timestamp = BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp; const epochChainId = "eip155:1" as Caip2ChainId; const blockNumber = await service.getEpochBlockNumber(timestamp, epochChainId); @@ -90,9 +150,24 @@ describe("BlockNumberService", () => { }); it("fails if the chain has no provider assigned", async () => { - const service = new BlockNumberService(rpcUrls, logger); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; + + const service = new BlockNumberService( + rpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ); - const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const timestamp = BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp; const epochChainId = "eip155:42161" as Caip2ChainId; await expect(service.getEpochBlockNumber(timestamp, epochChainId)).rejects.toThrow( @@ -116,8 +191,23 @@ describe("BlockNumberService", () => { ["eip155:1", ["http://localhost:8545"]], ]); - const service = new BlockNumberService(rpcUrls, logger); - const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; + + const service = new BlockNumberService( + rpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ); + const timestamp = BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp; await expect(service.getEpochBlockNumber(timestamp, "eip155:1")).rejects.toBeDefined(); @@ -145,6 +235,13 @@ describe("BlockNumberService", () => { ]), ); + const mockNotificationService: NotificationService = { + send: vi.fn().mockResolvedValue(undefined), + sendOrThrow: vi.fn().mockResolvedValue(undefined), + createErrorMessage: vi.fn().mockReturnValue({ title: "Test", description: "Test" }), + sendError: vi.fn().mockResolvedValue(undefined), + }; + beforeEach(() => { spyWithDummyProviders(dummyProviders); }); @@ -154,9 +251,24 @@ describe("BlockNumberService", () => { }); it("returns the chains' epoch block numbers", async () => { - const service = new BlockNumberService(rpcUrls, logger); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; + + const service = new BlockNumberService( + rpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ); - const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const timestamp = BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp; const epochChains = ["eip155:1", "eip155:137"] as Caip2ChainId[]; const blockNumbers = await service.getEpochBlockNumbers(timestamp, epochChains); @@ -168,9 +280,24 @@ describe("BlockNumberService", () => { }); it("fails if some input chain has no provider assigned", async () => { - const service = new BlockNumberService(rpcUrls, logger); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; - const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const service = new BlockNumberService( + rpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ); + + const timestamp = BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp; const epochChains = ["eip155:1", "eip155:42161"] as Caip2ChainId[]; await expect(service.getEpochBlockNumbers(timestamp, epochChains)).rejects.toThrow( @@ -194,8 +321,23 @@ describe("BlockNumberService", () => { ["eip155:1", ["http://localhost:8545"]], ]); - const service = new BlockNumberService(rpcUrls, logger); - const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const blockmetaConfig = { + baseUrl: new URL("http://localhost:443"), + servicePaths: { + block: "/block", + blockByTime: "/blockByTime", + }, + bearerToken: "bearer-token", + bearerTokenExpirationWindow: 300, + }; + + const service = new BlockNumberService( + rpcUrls, + blockmetaConfig, + logger, + mockNotificationService, + ); + const timestamp = BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)) as UnixTimestamp; await expect( service.getEpochBlockNumbers(timestamp, ["eip155:1"]), @@ -204,19 +346,18 @@ describe("BlockNumberService", () => { vi.clearAllMocks(); }); }); -}); -function spyWithDummyProviders(providersResults) { - type buildProviderArgs = Parameters; - const dummyProvider = (chainId: buildProviderArgs[0], client: buildProviderArgs[1]) => { - const provider = new providersResults[chainId].providerClass(client, {}); + function spyWithDummyProviders(providersResults: Record) { + const dummyProvider = (chainId: Caip2ChainId, client: any) => { + const provider = new providersResults[chainId].providerClass(client, {}); - provider.getEpochBlockNumber = vi - .fn() - .mockImplementation(providersResults[chainId].getEpochBlockNumber); + provider.getEpochBlockNumber = vi + .fn() + .mockImplementation(providersResults[chainId].getEpochBlockNumber); - return provider; - }; + return provider; + }; - vi.spyOn(BlockNumberProviderFactory, "buildProvider").mockImplementation(dummyProvider); -} + vi.spyOn(BlockNumberProviderFactory, "buildProvider").mockImplementation(dummyProvider); + } +}); diff --git a/packages/shared/src/services/index.ts b/packages/shared/src/services/index.ts index 9ccb0e95..954d4ae7 100644 --- a/packages/shared/src/services/index.ts +++ b/packages/shared/src/services/index.ts @@ -2,3 +2,4 @@ export * from "./hexUtils.js"; export * from "./caip2Utils.js"; export * from "./logger.js"; export * from "./stringify.js"; +export * from "./notificationService.js"; diff --git a/packages/automated-dispute/src/interfaces/notificationService.ts b/packages/shared/src/services/notificationService.ts similarity index 100% rename from packages/automated-dispute/src/interfaces/notificationService.ts rename to packages/shared/src/services/notificationService.ts