import { describe, it, expect, beforeEach, vi } from 'vitest' import { TronTransactionBuilder } from './tron-transaction-builder' import type { TronWeb } from '../../tron-discovery' import type { TronNativeTransferRequest, TronTRC20TransferRequest } from '@meshconnect/uwc-types' describe('TronTransactionBuilder', () => { let builder: TronTransactionBuilder let mockTronWeb: TronWeb beforeEach(() => { vi.clearAllMocks() builder = new TronTransactionBuilder() mockTronWeb = { trx: { sign: vi.fn(), signMessageV2: vi.fn(), sendRawTransaction: vi.fn() }, transactionBuilder: { sendTrx: vi.fn(), triggerSmartContract: vi.fn() }, toHex: vi.fn() } as any }) describe('buildNativeTransfer', () => { it('should build a native TRX transfer transaction', async () => { const mockTransaction = { txID: 'abc123', raw_data: {} } vi.mocked(mockTronWeb.transactionBuilder.sendTrx).mockResolvedValue( mockTransaction ) const request: TronNativeTransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: 1000000 // 1 TRX in SUN } const result = await builder.buildNativeTransfer(mockTronWeb, request) expect(mockTronWeb.transactionBuilder.sendTrx).toHaveBeenCalledWith( 'TToAddress456', 1000000, 'TFromAddress123' ) expect(result).toEqual(mockTransaction) }) it('should handle large amounts', async () => { const mockTransaction = { txID: 'large123' } vi.mocked(mockTronWeb.transactionBuilder.sendTrx).mockResolvedValue( mockTransaction ) const request: TronNativeTransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: 100000000000 // 100,000 TRX } const result = await builder.buildNativeTransfer(mockTronWeb, request) expect(result).toEqual(mockTransaction) }) it('rejects a bigint amount beyond the safe-integer range (no silent precision loss)', async () => { const request: TronNativeTransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: BigInt(Number.MAX_SAFE_INTEGER) + 2n } await expect( builder.buildNativeTransfer(mockTronWeb, request) ).rejects.toThrow() expect(mockTronWeb.transactionBuilder.sendTrx).not.toHaveBeenCalled() }) it('should throw on transactionBuilder error', async () => { vi.mocked(mockTronWeb.transactionBuilder.sendTrx).mockRejectedValue( new Error('Insufficient balance') ) const request: TronNativeTransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: 1000000 } await expect( builder.buildNativeTransfer(mockTronWeb, request) ).rejects.toThrow() }) }) describe('buildTRC20Transfer', () => { it('should build a TRC20 token transfer transaction', async () => { const mockTransaction = { txID: 'trc20-123', raw_data: {} } vi.mocked( mockTronWeb.transactionBuilder.triggerSmartContract ).mockResolvedValue({ transaction: mockTransaction, result: { result: true } }) const request: TronTRC20TransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: 1000000, contractAddress: 'TContractAddress789' } const result = await builder.buildTRC20Transfer(mockTronWeb, request) expect( mockTronWeb.transactionBuilder.triggerSmartContract ).toHaveBeenCalledWith( 'TContractAddress789', 'transfer(address,uint256)', {}, [ { type: 'address', value: 'TToAddress456' }, // uint256 is passed as a string to preserve precision for large amounts { type: 'uint256', value: '1000000' } ], 'TFromAddress123' ) expect(result).toEqual(mockTransaction) }) it('rejects a fractional TRC20 amount before building (no wrong uint256)', async () => { const request: TronTRC20TransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: 1.5, contractAddress: 'TContractAddress789' } await expect( builder.buildTRC20Transfer(mockTronWeb, request) ).rejects.toThrow() expect( mockTronWeb.transactionBuilder.triggerSmartContract ).not.toHaveBeenCalled() }) it('should throw when triggerSmartContract result is false', async () => { vi.mocked( mockTronWeb.transactionBuilder.triggerSmartContract ).mockResolvedValue({ transaction: {}, result: { result: false } }) const request: TronTRC20TransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: 1000000, contractAddress: 'TContractAddress789' } await expect( builder.buildTRC20Transfer(mockTronWeb, request) ).rejects.toThrow() }) it('should throw on triggerSmartContract error', async () => { vi.mocked( mockTronWeb.transactionBuilder.triggerSmartContract ).mockRejectedValue(new Error('Contract not found')) const request: TronTRC20TransferRequest = { from: 'TFromAddress123', to: 'TToAddress456', amount: 500, contractAddress: 'TBadContract' } await expect( builder.buildTRC20Transfer(mockTronWeb, request) ).rejects.toThrow() }) }) })