From 6a919265c1b5fcaa95b14cc1dfa70accffbe6862 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:17:16 +0100 Subject: [PATCH 1/2] fix: detect TRX sends in user-initiated swap transactions [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes incomplete transaction parsing for TRX → USDT swaps where only the USDT receive was shown but the TRX send was missing. The issue occurred when users swap through a router contract. The transaction flow is: 1. User initiates swap by calling router contract 2. Router sends user's TRX to swap pool (internal transaction) 3. Swap pool sends USDT back to user Previously, internal transactions were only detected if the caller or recipient matched the user's pubkey. This missed the TRX send because the router contract was the caller, not the user. The fix extracts the transaction initiator from raw_data.contract[0] and uses it to identify user-initiated internal transfers. When a contract sends TRX on behalf of the user, we now create a Send transfer with the user as the "from" address. Added diagnostic logging to track RPC responses and transfer parsing for debugging. Logs will be removed after testing confirms the fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 (1M context) --- .../src/tron/TronChainAdapter.ts | 75 +++++++++++++++++-- packages/unchained-client/src/tron/api.ts | 12 +++ 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/packages/chain-adapters/src/tron/TronChainAdapter.ts b/packages/chain-adapters/src/tron/TronChainAdapter.ts index 106dbb43a17..263acab7660 100644 --- a/packages/chain-adapters/src/tron/TronChainAdapter.ts +++ b/packages/chain-adapters/src/tron/TronChainAdapter.ts @@ -583,8 +583,26 @@ export class ChainAdapter implements IChainAdapter { const parsedTx = this.parse(tx, pubkey) + const txInitiator = tx.raw_data?.contract?.[0]?.parameter?.value?.owner_address + const trc20Transfers = this.parseTRC20Transfers(tx, pubkey) - const internalTrxTransfers = this.parseInternalTrxTransfers(tx, pubkey) + const internalTrxTransfers = this.parseInternalTrxTransfers(tx, pubkey, txInitiator) + + console.log('[TRON_FINAL] Combined transfers:', JSON.stringify({ + txid: tx.txid, + pubkey, + nativeTransferCount: parsedTx.transfers.length, + trc20TransferCount: trc20Transfers.length, + internalTransferCount: internalTrxTransfers.length, + totalTransferCount: parsedTx.transfers.length + trc20Transfers.length + internalTrxTransfers.length, + allTransfers: [...parsedTx.transfers, ...trc20Transfers, ...internalTrxTransfers].map(t => ({ + type: t.type, + assetId: t.assetId, + from: t.from, + to: t.to, + value: t.value, + })), + }, null, 2)) return { ...parsedTx, @@ -605,6 +623,14 @@ export class ChainAdapter implements IChainAdapter { type: TransferType value: string }[] { + console.log('[TRON_TRC20] Entry check:', JSON.stringify({ + txid: tx.txid, + hasLogs: !!tx.log, + logCount: tx.log?.length || 0, + contractRet: tx.ret?.[0]?.contractRet, + willProcess: tx.log && tx.log.length > 0 && tx.ret?.[0]?.contractRet === 'SUCCESS', + }, null, 2)) + if (!tx.log || tx.log.length === 0) return [] if (tx.ret?.[0]?.contractRet !== 'SUCCESS') return [] @@ -623,6 +649,13 @@ export class ChainAdapter implements IChainAdapter { const tronWeb = new TronWeb({ fullHost: this.rpcUrl }) for (const log of tx.log) { + console.log('[TRON_TRC20] Processing log:', JSON.stringify({ + address: log.address, + topics: log.topics, + data: log.data, + dataLength: log.data?.length, + }, null, 2)) + try { if (!log.topics || log.topics.length !== 3) continue if (log.topics[0] !== TRANSFER_EVENT_SIGNATURE) continue @@ -643,6 +676,17 @@ export class ChainAdapter implements IChainAdapter { const value = BigInt('0x' + log.data).toString() const contractAddress = log.address + console.log('[TRON_TRC20] Parsed transfer:', JSON.stringify({ + fromAddress, + toAddress, + pubkey, + isSend, + isReceive, + value, + contractAddress, + assetId: `${this.chainId}/trc20:${contractAddress}`, + }, null, 2)) + if (isSend) { transfers.push({ assetId: `${this.chainId}/trc20:${contractAddress}`, @@ -673,6 +717,7 @@ export class ChainAdapter implements IChainAdapter { private parseInternalTrxTransfers( tx: unchained.tron.TronTx, pubkey: string, + txInitiator?: string, ): { assetId: AssetId from: string[] @@ -680,6 +725,15 @@ export class ChainAdapter implements IChainAdapter { type: TransferType value: string }[] { + console.log('[TRON_INTERNAL] Entry check:', JSON.stringify({ + txid: tx.txid, + hasInternalTx: !!tx.internal_transactions, + count: tx.internal_transactions?.length || 0, + contractRet: tx.ret?.[0]?.contractRet, + txInitiator, + willProcess: tx.internal_transactions && tx.internal_transactions.length > 0 && tx.ret?.[0]?.contractRet === 'SUCCESS', + }, null, 2)) + if (!tx.internal_transactions || tx.internal_transactions.length === 0) return [] if (tx.ret?.[0]?.contractRet !== 'SUCCESS') return [] @@ -711,12 +765,11 @@ export class ChainAdapter implements IChainAdapter { const value = String(callInfo.callValue) - const isSend = caller_address === pubkey - const isReceive = transferTo_address === pubkey - - if (!isSend && !isReceive) continue + const isDirectSend = caller_address === pubkey + const isDirectReceive = transferTo_address === pubkey + const isInitiatedByUser = txInitiator === pubkey && caller_address !== pubkey - if (isSend) { + if (isDirectSend) { transfers.push({ assetId: this.assetId, from: [caller_address], @@ -724,9 +777,17 @@ export class ChainAdapter implements IChainAdapter { type: TransferType.Send, value, }) + } else if (isInitiatedByUser) { + transfers.push({ + assetId: this.assetId, + from: [txInitiator], + to: [transferTo_address], + type: TransferType.Send, + value, + }) } - if (isReceive) { + if (isDirectReceive) { transfers.push({ assetId: this.assetId, from: [caller_address], diff --git a/packages/unchained-client/src/tron/api.ts b/packages/unchained-client/src/tron/api.ts index 79e0f59d5ae..3d0c1a80e09 100644 --- a/packages/unchained-client/src/tron/api.ts +++ b/packages/unchained-client/src/tron/api.ts @@ -160,6 +160,18 @@ export class TronApi { internal_transactions = info.internal_transactions || [] } + console.log('[TRON_RPC] Transaction fetched:', JSON.stringify({ + txID: tx.txID, + blockNumber, + contractCount: tx.raw_data?.contract?.length || 0, + contractTypes: tx.raw_data?.contract?.map(c => c.type) || [], + logCount: log.length, + internalTxCount: internal_transactions.length, + contractRet: tx.ret?.[0]?.contractRet, + rawLog: log, + rawInternalTransactions: internal_transactions, + }, null, 2)) + return { ...tx, txid: tx.txID, From eb0b15e7820460fd84107c7624f448b19bea68e4 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:52:39 +0100 Subject: [PATCH 2/2] chore: remove diagnostic logging from Tron adapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up console logs added during investigation. All logs removed: - RPC response logging in api.ts - TRC20 transfer parsing logs - Internal transfer parsing logs - Final combined transfer logs Core fix remains: detecting TRX sends in user-initiated swap transactions by correlating transaction initiator with internal transfers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 (1M context) --- .../src/tron/TronChainAdapter.ts | 51 ------------------- packages/unchained-client/src/tron/api.ts | 12 ----- 2 files changed, 63 deletions(-) diff --git a/packages/chain-adapters/src/tron/TronChainAdapter.ts b/packages/chain-adapters/src/tron/TronChainAdapter.ts index 263acab7660..bd0c6cb53d7 100644 --- a/packages/chain-adapters/src/tron/TronChainAdapter.ts +++ b/packages/chain-adapters/src/tron/TronChainAdapter.ts @@ -588,22 +588,6 @@ export class ChainAdapter implements IChainAdapter { const trc20Transfers = this.parseTRC20Transfers(tx, pubkey) const internalTrxTransfers = this.parseInternalTrxTransfers(tx, pubkey, txInitiator) - console.log('[TRON_FINAL] Combined transfers:', JSON.stringify({ - txid: tx.txid, - pubkey, - nativeTransferCount: parsedTx.transfers.length, - trc20TransferCount: trc20Transfers.length, - internalTransferCount: internalTrxTransfers.length, - totalTransferCount: parsedTx.transfers.length + trc20Transfers.length + internalTrxTransfers.length, - allTransfers: [...parsedTx.transfers, ...trc20Transfers, ...internalTrxTransfers].map(t => ({ - type: t.type, - assetId: t.assetId, - from: t.from, - to: t.to, - value: t.value, - })), - }, null, 2)) - return { ...parsedTx, transfers: [...parsedTx.transfers, ...trc20Transfers, ...internalTrxTransfers], @@ -623,14 +607,6 @@ export class ChainAdapter implements IChainAdapter { type: TransferType value: string }[] { - console.log('[TRON_TRC20] Entry check:', JSON.stringify({ - txid: tx.txid, - hasLogs: !!tx.log, - logCount: tx.log?.length || 0, - contractRet: tx.ret?.[0]?.contractRet, - willProcess: tx.log && tx.log.length > 0 && tx.ret?.[0]?.contractRet === 'SUCCESS', - }, null, 2)) - if (!tx.log || tx.log.length === 0) return [] if (tx.ret?.[0]?.contractRet !== 'SUCCESS') return [] @@ -649,13 +625,6 @@ export class ChainAdapter implements IChainAdapter { const tronWeb = new TronWeb({ fullHost: this.rpcUrl }) for (const log of tx.log) { - console.log('[TRON_TRC20] Processing log:', JSON.stringify({ - address: log.address, - topics: log.topics, - data: log.data, - dataLength: log.data?.length, - }, null, 2)) - try { if (!log.topics || log.topics.length !== 3) continue if (log.topics[0] !== TRANSFER_EVENT_SIGNATURE) continue @@ -676,17 +645,6 @@ export class ChainAdapter implements IChainAdapter { const value = BigInt('0x' + log.data).toString() const contractAddress = log.address - console.log('[TRON_TRC20] Parsed transfer:', JSON.stringify({ - fromAddress, - toAddress, - pubkey, - isSend, - isReceive, - value, - contractAddress, - assetId: `${this.chainId}/trc20:${contractAddress}`, - }, null, 2)) - if (isSend) { transfers.push({ assetId: `${this.chainId}/trc20:${contractAddress}`, @@ -725,15 +683,6 @@ export class ChainAdapter implements IChainAdapter { type: TransferType value: string }[] { - console.log('[TRON_INTERNAL] Entry check:', JSON.stringify({ - txid: tx.txid, - hasInternalTx: !!tx.internal_transactions, - count: tx.internal_transactions?.length || 0, - contractRet: tx.ret?.[0]?.contractRet, - txInitiator, - willProcess: tx.internal_transactions && tx.internal_transactions.length > 0 && tx.ret?.[0]?.contractRet === 'SUCCESS', - }, null, 2)) - if (!tx.internal_transactions || tx.internal_transactions.length === 0) return [] if (tx.ret?.[0]?.contractRet !== 'SUCCESS') return [] diff --git a/packages/unchained-client/src/tron/api.ts b/packages/unchained-client/src/tron/api.ts index 3d0c1a80e09..79e0f59d5ae 100644 --- a/packages/unchained-client/src/tron/api.ts +++ b/packages/unchained-client/src/tron/api.ts @@ -160,18 +160,6 @@ export class TronApi { internal_transactions = info.internal_transactions || [] } - console.log('[TRON_RPC] Transaction fetched:', JSON.stringify({ - txID: tx.txID, - blockNumber, - contractCount: tx.raw_data?.contract?.length || 0, - contractTypes: tx.raw_data?.contract?.map(c => c.type) || [], - logCount: log.length, - internalTxCount: internal_transactions.length, - contractRet: tx.ret?.[0]?.contractRet, - rawLog: log, - rawInternalTransactions: internal_transactions, - }, null, 2)) - return { ...tx, txid: tx.txID,