diff --git a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts index 10e4e2536f..a77b755739 100644 --- a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts +++ b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts @@ -4,6 +4,7 @@ import type { TransactionScanResponse } from '@blockaid/client/resources/evm/evm import type { Address } from 'viem'; import { faker } from '@faker-js/faker'; import type Blockaid from '@blockaid/client'; +import type { ILoggingService } from '@/logging/logging.interface'; const createMockWithResponse = ( data: TransactionScanResponse, @@ -36,13 +37,17 @@ const mockBlockaidClient = { }, } as jest.MockedObjectDeep; +const mockLoggingService = { + info: jest.fn(), +} as jest.MockedObjectDeep; + describe('BlockaidApi', () => { let service: BlockaidApi; beforeEach(() => { jest.resetAllMocks(); - service = new BlockaidApi(); + service = new BlockaidApi(mockLoggingService); // eslint-disable-next-line @typescript-eslint/no-explicit-any (service as any).blockaidClient = mockBlockaidClient; }); @@ -99,9 +104,14 @@ describe('BlockaidApi', () => { const request_id = faker.string.uuid(); const mockScanResponse: TransactionScanResponse = { - block: faker.string.numeric(), chain: `0x${chainId}`, - status: 'Success', + block: faker.string.numeric(), + validation: { + status: 'Success', + result_type: 'Benign', + description: 'No issues detected', + features: [], + }, } as TransactionScanResponse; mockBlockaidClient.evm.jsonRpc.scan.mockReturnValue( @@ -132,6 +142,20 @@ describe('BlockaidApi', () => { ...mockScanResponse, request_id, }); + expect(mockLoggingService.info).toHaveBeenCalledWith({ + message: 'Blockaid scan response', + response: { + chain: mockScanResponse.chain, + request_id, + validation: { + status: mockScanResponse.validation?.status, + result_type: mockScanResponse.validation?.result_type, + description: mockScanResponse.validation?.description, + features: [], + }, + simulation: undefined, + }, + }); }); it('should call blockaid client without domain parameter/ with non_dapp', async () => { @@ -140,7 +164,6 @@ describe('BlockaidApi', () => { const mockScanResponse: TransactionScanResponse = { block: faker.string.numeric(), chain: `0x${chainId}`, - status: 'Success', } as TransactionScanResponse; mockBlockaidClient.evm.jsonRpc.scan.mockReturnValue( @@ -176,7 +199,6 @@ describe('BlockaidApi', () => { const mockScanResponse: TransactionScanResponse = { block: faker.string.numeric(), chain: `0x${chainId}`, - status: 'Success', } as TransactionScanResponse; mockBlockaidClient.evm.jsonRpc.scan.mockReturnValue( @@ -194,6 +216,16 @@ describe('BlockaidApi', () => { ...mockScanResponse, request_id: undefined, }); + + expect(mockLoggingService.info).toHaveBeenCalledWith({ + message: 'Blockaid scan response', + response: { + chain: mockScanResponse.chain, + request_id: undefined, + validation: undefined, + simulation: undefined, + }, + }); }); it('should forward errors from blockaid client', async () => { diff --git a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts index a36f2eba6e..da1515f777 100644 --- a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts +++ b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts @@ -6,14 +6,19 @@ import { ReportEvent } from '@/modules/safe-shield/entities/dtos/report-false-re import { BLOCKAID_REQUEST_ID_HEADER } from '@/modules/safe-shield/threat-analysis/blockaid/blockaid-api.constants'; import Blockaid from '@blockaid/client'; import { JsonRpcScanParams } from '@blockaid/client/resources/evm/json-rpc'; -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { Address, numberToHex } from 'viem'; +import { ILoggingService, LoggingService } from '@/logging/logging.interface'; +import { BlockaidScanLogSchema } from '@/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema'; @Injectable() export class BlockaidApi implements IBlockaidApi { private readonly blockaidClient: Blockaid; - constructor() { + constructor( + @Inject(LoggingService) + private readonly loggingService: ILoggingService, + ) { this.blockaidClient = new Blockaid(); } @@ -54,6 +59,7 @@ export class BlockaidApi implements IBlockaidApi { const request_id = response.headers.get(BLOCKAID_REQUEST_ID_HEADER) ?? undefined; + this.logScanResponse({ ...data, request_id }); return { ...data, request_id }; } @@ -71,4 +77,14 @@ export class BlockaidApi implements IBlockaidApi { }, }); } + + private logScanResponse( + response: TransactionScanResponseWithRequestId, + ): void { + const logData = BlockaidScanLogSchema.parse({ ...response }); + this.loggingService.info({ + message: 'Blockaid scan response', + response: logData, + }); + } } diff --git a/src/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema.ts b/src/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema.ts new file mode 100644 index 0000000000..a8440289bb --- /dev/null +++ b/src/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema.ts @@ -0,0 +1,28 @@ +import { z } from 'zod'; + +export const BlockaidScanLogSchema = z.object({ + chain: z.string(), + request_id: z.string().optional(), + validation: z + .object({ + status: z.string(), + result_type: z.string(), + description: z.string().optional(), + features: z + .array( + z.object({ + type: z.string(), + feature_id: z.string(), + }), + ) + .optional(), + }) + .optional(), + simulation: z + .object({ + status: z.string(), + }) + .optional(), +}); + +export type BlockaidScanLog = z.infer;