import { Controller, Post, Body, Get, Param } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiProperty, ApiParam } from '@nestjs/swagger'; import { IsString, IsNotEmpty, Matches, IsNumberString } from 'class-validator'; import { Erc20TransferService, TransferResult, TokenType } from '@/domain/services/erc20-transfer.service'; import { ChainTypeEnum } from '@/domain/enums'; /** * dUSDT 转账请求 DTO */ class TransferDusdtDto { @ApiProperty({ description: '接收者 Kava 地址', example: '0x1234567890abcdef1234567890abcdef12345678' }) @IsString() @IsNotEmpty() @Matches(/^0x[a-fA-F0-9]{40}$/, { message: 'Invalid EVM address format' }) toAddress: string; @ApiProperty({ description: '转账金额(人类可读格式)', example: '100.5' }) @IsString() @IsNotEmpty() @IsNumberString({}, { message: 'Amount must be a valid number string' }) amount: string; } /** * 转账结果响应 DTO */ class TransferResponseDto { @ApiProperty({ description: '是否成功' }) success: boolean; @ApiProperty({ description: '交易哈希', required: false }) txHash?: string; @ApiProperty({ description: '错误信息', required: false }) error?: string; @ApiProperty({ description: '消耗的 Gas', required: false }) gasUsed?: string; @ApiProperty({ description: '区块高度', required: false }) blockNumber?: number; } /** * 余额响应 DTO */ class BalanceResponseDto { @ApiProperty({ description: '热钱包地址' }) address: string; @ApiProperty({ description: 'dUSDT 余额' }) balance: string; @ApiProperty({ description: '链类型' }) chain: string; } /** * dUSDT 转账控制器 * 供 trading-service C2C Bot 调用 */ @ApiTags('Transfer') @Controller('transfer') export class TransferController { constructor(private readonly erc20TransferService: Erc20TransferService) {} @Post('dusdt') @ApiOperation({ summary: '转账 dUSDT 到指定地址' }) @ApiResponse({ status: 200, description: '转账结果', type: TransferResponseDto }) @ApiResponse({ status: 400, description: '参数错误' }) @ApiResponse({ status: 500, description: '转账失败' }) async transferDusdt(@Body() dto: TransferDusdtDto): Promise { const result: TransferResult = await this.erc20TransferService.transferUsdt( ChainTypeEnum.KAVA, dto.toAddress, dto.amount, ); return { success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, blockNumber: result.blockNumber, }; } @Get('dusdt/balance') @ApiOperation({ summary: '查询热钱包 dUSDT 余额' }) @ApiResponse({ status: 200, description: '余额信息', type: BalanceResponseDto }) async getHotWalletBalance(): Promise { const address = this.erc20TransferService.getHotWalletAddress(ChainTypeEnum.KAVA); const balance = await this.erc20TransferService.getHotWalletBalance(ChainTypeEnum.KAVA); return { address: address || '', balance, chain: 'KAVA', }; } @Get('status') @ApiOperation({ summary: '检查转账服务状态' }) @ApiResponse({ status: 200, description: '服务状态' }) async getStatus(): Promise<{ configured: boolean; hotWalletAddress: string | null }> { const configured = this.erc20TransferService.isConfigured(ChainTypeEnum.KAVA); const hotWalletAddress = this.erc20TransferService.getHotWalletAddress(ChainTypeEnum.KAVA); return { configured, hotWalletAddress, }; } // ============ eUSDT (积分股) 转账接口 ============ @Post('eusdt') @ApiOperation({ summary: '转账 eUSDT(积分股)到指定地址' }) @ApiResponse({ status: 200, description: '转账结果', type: TransferResponseDto }) @ApiResponse({ status: 400, description: '参数错误' }) @ApiResponse({ status: 500, description: '转账失败' }) async transferEusdt(@Body() dto: TransferDusdtDto): Promise { const result: TransferResult = await this.erc20TransferService.transferToken( ChainTypeEnum.KAVA, 'EUSDT', dto.toAddress, dto.amount, ); return { success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, blockNumber: result.blockNumber, }; } @Get('eusdt/balance') @ApiOperation({ summary: '查询热钱包 eUSDT(积分股)余额' }) @ApiResponse({ status: 200, description: '余额信息', type: BalanceResponseDto }) async getEusdtBalance(): Promise { const address = this.erc20TransferService.getHotWalletAddress(ChainTypeEnum.KAVA); const balance = await this.erc20TransferService.getTokenBalance(ChainTypeEnum.KAVA, 'EUSDT'); return { address: address || '', balance, chain: 'KAVA', }; } // ============ fUSDT (积分值) 转账接口 ============ @Post('fusdt') @ApiOperation({ summary: '转账 fUSDT(积分值)到指定地址' }) @ApiResponse({ status: 200, description: '转账结果', type: TransferResponseDto }) @ApiResponse({ status: 400, description: '参数错误' }) @ApiResponse({ status: 500, description: '转账失败' }) async transferFusdt(@Body() dto: TransferDusdtDto): Promise { const result: TransferResult = await this.erc20TransferService.transferToken( ChainTypeEnum.KAVA, 'FUSDT', dto.toAddress, dto.amount, ); return { success: result.success, txHash: result.txHash, error: result.error, gasUsed: result.gasUsed, blockNumber: result.blockNumber, }; } @Get('fusdt/balance') @ApiOperation({ summary: '查询热钱包 fUSDT(积分值)余额' }) @ApiResponse({ status: 200, description: '余额信息', type: BalanceResponseDto }) async getFusdtBalance(): Promise { const address = this.erc20TransferService.getHotWalletAddress(ChainTypeEnum.KAVA); const balance = await this.erc20TransferService.getTokenBalance(ChainTypeEnum.KAVA, 'FUSDT'); return { address: address || '', balance, chain: 'KAVA', }; } }