import { Events, type IPortalApi, generateTraceId, sdkLogger, } from '@portal-hq/utils' import { BlockaidAddressScanRequest, BlockaidAddressScanResponse, BlockaidBulkTokenScanRequest, BlockaidBulkTokenScanResponse, BlockaidScanEvmTxRequest, BlockaidScanEvmTxResponse, BlockaidScanSolanaTxRequest, BlockaidScanSolanaTxResponse, BlockaidSiteScanRequest, BlockaidSiteScanResponse, } from './types' const BLOCKAID_BASE_PATH = '/api/v3/clients/me/integrations/blockaid' const BlockaidEndpoint = { evmScan: `${BLOCKAID_BASE_PATH}/evm/scan`, solanaScan: `${BLOCKAID_BASE_PATH}/solana/scan`, addressScan: `${BLOCKAID_BASE_PATH}/address/scan`, tokensScan: `${BLOCKAID_BASE_PATH}/tokens/scan`, urlScan: `${BLOCKAID_BASE_PATH}/url/scan`, } as const export interface IPortalBlockaidApi { scanEVMTx( params: BlockaidScanEvmTxRequest, ): Promise scanSolanaTx( params: BlockaidScanSolanaTxRequest, ): Promise scanAddress( params: BlockaidAddressScanRequest, ): Promise scanTokens( params: BlockaidBulkTokenScanRequest, ): Promise scanURL(params: BlockaidSiteScanRequest): Promise } export interface PortalBlockaidApiOptions { api: IPortalApi } export class PortalBlockaidApi implements IPortalBlockaidApi { private readonly api: IPortalApi constructor(options: PortalBlockaidApiOptions) { this.api = options.api } /** * Scans an EVM transaction for security threats * @param params - EVM transaction scan parameters * @returns Promise resolving to scan results */ public async scanEVMTx( params: BlockaidScanEvmTxRequest, ): Promise { const path = BlockaidEndpoint.evmScan const body = params const response = await this.post( path, body, ) await this.trackEvent(Events.BlockaidScanEVMTx, { path, chain: params.chain, }) return response } /** * Scans a Solana transaction for security threats * @param params - Solana transaction scan parameters * @returns Promise resolving to scan results */ public async scanSolanaTx( params: BlockaidScanSolanaTxRequest, ): Promise { const path = BlockaidEndpoint.solanaScan const body = params const response = await this.post( path, body, ) await this.trackEvent(Events.BlockaidScanSolanaTx, { path, chain: params.chain, transactionCount: params.transactions.length, }) return response } /** * Scans an address for security threats * @param params - Address scan parameters * @returns Promise resolving to scan results */ public async scanAddress( params: BlockaidAddressScanRequest, ): Promise { const path = BlockaidEndpoint.addressScan const body = params const response = await this.post( path, body, ) await this.trackEvent(Events.BlockaidScanAddress, { path, chain: params.chain, address: params.address, }) return response } /** * Scans multiple tokens for security threats * @param params - Token scan parameters * @returns Promise resolving to scan results */ public async scanTokens( params: BlockaidBulkTokenScanRequest, ): Promise { const path = BlockaidEndpoint.tokensScan const body = params const response = await this.post< BlockaidBulkTokenScanResponse, typeof body >(path, body) await this.trackEvent(Events.BlockaidScanTokens, { path, chain: params.chain, tokenCount: params.tokens.length, }) return response } /** * Scans a URL for phishing and malicious dApps * @param params - URL scan parameters * @returns Promise resolving to scan results */ public async scanURL( params: BlockaidSiteScanRequest, ): Promise { const path = BlockaidEndpoint.urlScan const body = params const response = await this.post( path, body, ) await this.trackEvent(Events.BlockaidScanURL, { path, url: params.url, }) return response } private async post(path: string, body: B): Promise { return this.api.requests.post(path, { headers: { Authorization: `Bearer ${this.api.apiKey}`, Accept: 'application/json', 'Content-Type': 'application/json', }, body, traceId: generateTraceId(), }) } private async trackEvent( event: string, properties: Record, ): Promise { try { await this.api.track(event, properties) } catch (error) { sdkLogger.warn( `[PortalBlockaidApi] Failed to track event "${event}":`, error, ) } } } export default PortalBlockaidApi