import { NestFactory } from '@nestjs/core'; import { ValidationPipe, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; import { AppModule } from './app.module'; import { Erc20TransferService } from './domain/services/erc20-transfer.service'; import { ChainTypeEnum } from './domain/enums'; async function bootstrap() { const logger = new Logger('Bootstrap'); const app = await NestFactory.create(AppModule); const configService = app.get(ConfigService); // 全局验证管道 app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true, forbidNonWhitelisted: true, }), ); // CORS app.enableCors(); // 全局 API 前缀 const apiPrefix = configService.get('app.apiPrefix', 'api/v1'); app.setGlobalPrefix(apiPrefix); // Swagger 文档 const swaggerConfig = new DocumentBuilder() .setTitle('Mining Blockchain Service API') .setDescription('Mining 区块链服务 - C2C Bot dUSDT 转账') .setVersion('1.0') .addTag('Health', '健康检查') .addTag('Transfer', 'dUSDT 转账') .build(); const document = SwaggerModule.createDocument(app, swaggerConfig); SwaggerModule.setup('api', app, document); // Kafka 微服务(用于 MPC 签名通信) const kafkaBrokers = configService.get('kafka.brokers') || ['localhost:9092']; const kafkaGroupId = configService.get('kafka.groupId') || 'mining-blockchain-service-group'; app.connectMicroservice({ transport: Transport.KAFKA, options: { client: { clientId: 'mining-blockchain-service', brokers: kafkaBrokers, }, consumer: { groupId: kafkaGroupId, }, }, }); // 启动微服务 await app.startAllMicroservices(); logger.log('Kafka microservice started for MPC signing'); // 验证关键配置 validateBlockchainConfig(configService, logger); // 启动 HTTP 服务 const port = configService.get('app.port', 3020); await app.listen(port); logger.log(`Mining Blockchain service is running on port ${port}`); logger.log(`Swagger docs available at http://localhost:${port}/api`); // 异步检查 fUSDT 注入钱包余额(不阻塞启动) checkInjectionWalletBalance(app, logger).catch(err => { logger.error('[STARTUP] Failed to check injection wallet balance', err); }); } /** * 验证区块链关键配置 * 如果缺少必要配置,输出明确的错误日志 */ function validateBlockchainConfig(configService: ConfigService, logger: Logger) { const warnings: string[] = []; // eUSDT (积分股) 做市商钱包配置验证 const eusdtMarketMakerAddress = configService.get('blockchain.eusdtMarketMaker.walletAddress'); const eusdtMarketMakerUsername = configService.get('blockchain.eusdtMarketMaker.mpcUsername'); logger.log(`[CONFIG] eUSDT (积分股) 做市商钱包配置:`); if (eusdtMarketMakerAddress) { logger.log(` - 地址: ${eusdtMarketMakerAddress}`); } else { warnings.push('[CONFIG WARNING] EUSDT_MARKET_MAKER_ADDRESS 未配置!eUSDT 充值监控功能将不可用。'); } if (!eusdtMarketMakerUsername) { warnings.push('[CONFIG WARNING] EUSDT_MARKET_MAKER_USERNAME 未配置!eUSDT 转账功能将不可用。'); } // fUSDT (积分值) 做市商钱包配置验证 const fusdtMarketMakerAddress = configService.get('blockchain.fusdtMarketMaker.walletAddress'); const fusdtMarketMakerUsername = configService.get('blockchain.fusdtMarketMaker.mpcUsername'); logger.log(`[CONFIG] fUSDT (积分值) 做市商钱包配置:`); if (fusdtMarketMakerAddress) { logger.log(` - 地址: ${fusdtMarketMakerAddress}`); } else { warnings.push('[CONFIG WARNING] FUSDT_MARKET_MAKER_ADDRESS 未配置!fUSDT 充值监控功能将不可用。'); } if (!fusdtMarketMakerUsername) { warnings.push('[CONFIG WARNING] FUSDT_MARKET_MAKER_USERNAME 未配置!fUSDT 转账功能将不可用。'); } // C2C Bot 热钱包配置验证 const c2cBotWalletAddress = configService.get('C2C_BOT_WALLET_ADDRESS'); const c2cBotWalletUsername = configService.get('C2C_BOT_WALLET_USERNAME'); logger.log(`[CONFIG] C2C Bot 热钱包配置:`); if (c2cBotWalletAddress) { logger.log(` - 地址: ${c2cBotWalletAddress}`); } else { warnings.push('[CONFIG WARNING] C2C_BOT_WALLET_ADDRESS 未配置!C2C Bot 转账功能将不可用。'); } if (!c2cBotWalletUsername) { warnings.push('[CONFIG WARNING] C2C_BOT_WALLET_USERNAME 未配置!C2C Bot MPC 签名功能将不可用。'); } // fUSDT 注入钱包配置验证(认种自动转积分值到做市商) const fusdtInjectionAddress = configService.get('FUSDT_INJECTION_WALLET_ADDRESS'); const fusdtInjectionUsername = configService.get('FUSDT_INJECTION_WALLET_USERNAME'); logger.log(`[CONFIG] fUSDT 注入钱包配置(认种自动注入):`); if (fusdtInjectionAddress) { logger.log(` - 地址: ${fusdtInjectionAddress}`); } else { warnings.push('[CONFIG WARNING] FUSDT_INJECTION_WALLET_ADDRESS 未配置!认种自动 fUSDT 注入功能将不可用。'); } if (!fusdtInjectionUsername) { warnings.push('[CONFIG WARNING] FUSDT_INJECTION_WALLET_USERNAME 未配置!认种自动 fUSDT 注入签名将不可用。'); } // KAVA 代币合约配置日志 const eUsdtContract = configService.get('blockchain.kava.eUsdtContract'); const fUsdtContract = configService.get('blockchain.kava.fUsdtContract'); const usdtContract = configService.get('blockchain.kava.usdtContract'); logger.log(`[CONFIG] KAVA 代币合约配置:`); logger.log(` - eUSDT (积分股): ${eUsdtContract || '未配置'}`); logger.log(` - fUSDT (积分值): ${fUsdtContract || '未配置'}`); logger.log(` - dUSDT (绿积分): ${usdtContract || '未配置'}`); // 输出所有警告 warnings.forEach(warning => logger.warn(warning)); if (warnings.length > 0) { logger.warn(`[CONFIG] 发现 ${warnings.length} 个配置警告,部分功能将不可用!`); } } /** * 检查 fUSDT 注入钱包余额 * 如果余额为 0,输出警告日志 */ async function checkInjectionWalletBalance(app: any, logger: Logger) { try { const erc20Service = app.get(Erc20TransferService); const injectionAddress = erc20Service.getFusdtInjectionAddress(); if (!injectionAddress) { logger.warn('[BALANCE] fUSDT 注入钱包未配置,跳过余额检查'); return; } logger.log(`[BALANCE] 正在查询 fUSDT 注入钱包余额: ${injectionAddress}`); const balance = await erc20Service.getFusdtInjectionTokenBalance(ChainTypeEnum.KAVA); const balanceNum = parseFloat(balance); if (balanceNum === 0) { logger.warn(`[BALANCE] ⚠️ fUSDT 注入钱包余额为 0!地址: ${injectionAddress}`); logger.warn(`[BALANCE] ⚠️ 认种自动注入将因余额不足而失败,请及时充值!`); } else { logger.log(`[BALANCE] fUSDT 注入钱包余额: ${balance} fUSDT`); } } catch (error) { logger.warn(`[BALANCE] 无法查询 fUSDT 注入钱包余额 (链可能未就绪): ${error instanceof Error ? error.message : error}`); } } bootstrap();