Skip to content

SBI-81: TON Jetton transfer parser #136

@Puneethkumarck

Description

@Puneethkumarck

Summary

Implement TonTransferParser — detects both USDT Jetton deposits and native TON transfers from TonCenter API v3 data.

Business Context

USDT is the dominant stablecoin on TON. This parser is the core value delivery of the TON adapter. Native TON transfer parsing enables the gas station use case (configurable via index-native-transfers: true).

Technical Context (Verified)

  • Jettons follow TEP-74 standard. A transfer involves 2-4 async transactions across shards.
  • TonCenter API v3 jetton/transfers endpoint abstracts shard complexity — returns pre-parsed transfers with jetton_master, source, destination, amount.
  • Must normalize addresses to raw format for bloom filter matching via TonAddressConverter.
  • USDT master: EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs (6 decimals)
  • Native TON: 9 decimals (1 TON = 1,000,000,000 nanoton)
  • No Circle USDC on TON as of April 2026

Implementation Guide

Files to Create

TonTransferParser.java (package-private)

@Slf4j
class TonTransferParser {
    private final Map<String, TonChainJettonConfig> jettonWhitelist; // keyed by normalized master address
    private final boolean indexNativeTransfers;
    private final int nativeDecimals;
    private final ChainId chainId;

    List<Transfer> parseJettonTransfers(List<TonJettonTransfer> jettonTransfers, long masterchainSeqno) {
        // For each jettonTransfer:
        //   normalizedMaster = TonAddressConverter.normalize(jettonTransfer.jetton_master())
        //   Skip if not in jettonWhitelist
        //   config = jettonWhitelist.get(normalizedMaster)
        //   fromAddress = TonAddressConverter.normalize(source)
        //   toAddress = TonAddressConverter.normalize(destination)
        //   Build Transfer(nativeTransfer=false, tokenContractAddress=normalizedMaster)
    }

    List<Transfer> parseNativeTransfers(List<TonTransaction> transactions, long masterchainSeqno) {
        // For each transaction where balance_change > 0 (incoming):
        //   toAddress = TonAddressConverter.normalize(transaction.account())
        //   Build Transfer(nativeTransfer=true, tokenSymbol="TON", decimals=9, logIndex=-1)
        //   NOTE: sender determination requires trace analysis — set fromAddress="" if unavailable
    }
}

Acceptance Criteria

Jetton Transfers

  • Matches jetton_master against whitelist (normalized to raw format)
  • Normalizes source and destination to raw format via TonAddressConverter
  • Returns Transfer with nativeTransfer=false, correct tokenSymbol, decimals from config
  • tokenContractAddress = normalized Jetton master address
  • Skips non-whitelisted Jetton masters

Native TON Transfers

  • Detects incoming native TON transfers (positive balance_change)
  • Returns Transfer with nativeTransfer=true, tokenSymbol="TON", decimals=9, logIndex=-1
  • Normalizes account address to raw format
  • Only runs when indexNativeTransfers=true

Testing

  • TestFixtures: TonFixtures.java with aTonJettonTransfer() and aTonTransaction() builders
  • Unit tests for Jetton parsing: whitelisted match, non-whitelisted skip
  • Unit tests for native parsing: positive balance detected, zero/negative skipped
  • ./gradlew build passes

Dependencies

  • SBI-79 (TonAddressConverter)
  • SBI-80 (TonApiClient + TonJettonTransfer/TonTransaction DTOs)

Phase & Batch

Phase 13 — TON, Batch 2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions