Skip to content

feat: add TON network support (native TON + USDT-Jetton) - #69

Open
vectorsss wants to merge 3 commits into
GMWalletApp:masterfrom
vectorsss:feat-ton-network-support
Open

feat: add TON network support (native TON + USDT-Jetton)#69
vectorsss wants to merge 3 commits into
GMWalletApp:masterfrom
vectorsss:feat-ton-network-support

Conversation

@vectorsss

Copy link
Copy Markdown

Summary

Adds TON (The Open Network) as a new supported network. Both native
TON and USDT-Jetton receiving paths work end-to-end on TON testnet
and have been verified against the Tether-issued USDT-Jetton master
on mainnet (decode-only). Related: #68 (frontend cashier UI).

What's included

Backend listener (src/model/service/ton_task.go,
src/task/listen_ton_job.go)

  • Polling listener that mirrors the Solana cron-tick shape; one master
    query per tick, decodes both native TON and Jetton transfer
    notifications.
  • Built on xssnick/tonutils-go v1.16.0 (native lite-server / ADNL
    protocol — no third-party indexer dependency, no API key needed).
  • Strict block-count confirmation gate (real master seqno advance,
    not wall-clock estimates) honouring chains.min_confirmations.
  • Retry-aware dedup: transient errors and not-yet-visible orders keep
    retrying; only terminal outcomes seed the dedup cache.
  • Plain-transfer body classifier: accepts empty body and
    0x00000000 / 0x2167da4b (TEP-1 encrypted comment) prefixes;
    rejects contract notifications carrying forwarding TON.
  • Address normalization: EQ.../UQ.../0:hex... collapse to one
    canonical form at the wallet and transaction_lock layers.

Seed data (src/model/dao/mdb_table_init.go)

  • chains row for ton, chain_tokens rows for USDT-Jetton master
    EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs (6 decimals) and
    native TON (9 decimals), rpc_nodes row pointing at
    ton-blockchain.github.io/global.config.json.
  • All seeds are idempotent (ON CONFLICT DO NOTHING).

Order-creation side

  • TON orders extend transaction_lock.expires_at by
    min_confirmations × 5s so the listener's confirmation gate can't
    outlive the lock it gates.

Test evidence

  • Unit tests covering: body opcode classifier (7 cases incl. plain,
    encrypted, jetton notification, NFT opcode), confirmation gate
    (first-sight defer, block-advance, no over-credit), TTL scaling +
    eviction, clamp policy for misconfigured min_confirmations, TON
    address canonicalization (EQ/UQ/raw), seed idempotency.
  • go vet ./... clean.
  • Testnet end-to-end: created a TON-network order via
    /payments/gmpay/v1/order/create-transaction, sent the exact
    actual_amount from a separate testnet wallet, observed the
    listener match the transaction_lock, call OrderProcessing,
    mark the order paid and write the on-chain tx hash.

Known limitation — frontend cashier

The compiled src/www/ admin SPA / cashier page is not affected by
backend changes alone. The cashier _trade_id-*.js has a hard-coded
chain metadata table (ut) that lacks a TON entry, and
src/www/images/chains/ has no ton.png. This renders TON orders
with the lowercase string ton and a missing logo. Functionally the
payment still completes — only the visual presentation is affected.

The frontend source for src/www/ does not appear to be in this
repository, so the fix needs to come from the upstream frontend
build. Opened as #68 for visibility.

Test plan

  • go build ./...
  • go vet ./...
  • go test ./model/... (new tests + existing pass)
  • Testnet end-to-end: native TON payment confirmed
  • Mainnet decode validated against a live treasury (178+ historic
    transactions correctly classified)
  • Maintainer review

🤖 Generated with Claude Code

vectorsss and others added 3 commits May 13, 2026 17:19
Adds the "ton" network alongside the existing Tron/EVM/Solana/Plasma
listeners. Uses xssnick/tonutils-go to talk to mainnet/testnet lite
servers; the rpc_nodes URL is the TON global.config.json endpoint.

- ton_task: polling-style scan of each owner's recent transactions,
  decoding internal messages into either Jetton TransferNotification
  payments (matched via the owner's deterministically-derived jetton
  wallet contract) or native TON value transfers.
- listen_ton_job: 5s cron job mirroring the Solana scheduler.
- Address normalization collapses the three TON surface forms
  (bounceable EQ, non-bounceable UQ, raw 0:hex) into one canonical
  storage key at the wallet_address and transaction_lock layers so
  admin input and chain-derived addresses match.
- API client and per-owner jetton wallet address are cached across
  ticks to keep lite-server round-trips bounded.

Enabling TON still requires admin-side seed rows in chains,
chain_tokens and rpc_nodes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ver config

Adds TON to the three idempotent seeders in mdb_table_init so a fresh
install boots with TON enabled and ready to receive payments. Existing
deployments pick up the new rows on next start; ON CONFLICT DO NOTHING
preserves any admin edits.

Seeded values:
- chains: ton (DisplayName "TON", MinConfirmations 1).
- chain_tokens: USDT Jetton master
  EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs at 6 decimals, plus a
  native TON row at 9 decimals.
- rpc_nodes: ton-blockchain.github.io/global.config.json. type=http
  describes how tonutils-go fetches the config; lite-server traffic
  itself runs over ADNL/TCP after that.

A new package test asserts the TON rows land and that a second seed
pass is a no-op. testutil also includes ton in its standard chains
list so IsChainEnabled checks remain consistent across the codebase.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hardens the TON listener against three classes of correctness gap
that could miss legitimate payments or settle unconfirmed ones.

- Outcome enum on processing. Only terminal outcomes
  (processed/irrelevant) seed the dedup cache; transient failures and
  not-yet-visible orders return retry so the next tick gets another
  shot instead of turning into a multi-hour blind spot.

- Block-count confirmation gate. First sight records the current
  masterchain seqno as a safe upper bound on the tx's anchor and
  defers; later sights settle once the live master seqno has advanced
  by min_confirmations. No wall-clock head-start (which would
  over-credit under cadence variance or clock skew). The scan cutoff
  extends by the gate's max wait so high min_confirmations values
  don't age out before settling. effectiveTonMinConfirmations clamps
  admin misconfigs with a warn, and the same clamped value extends
  the TON order's transaction_lock TTL via lockExpirationForNetwork
  so the lock can't expire under the gate.

- Plain-transfer body classifier. Native TON acceptance now requires
  an empty body or a 32-bit opcode of tonOpTextComment /
  tonOpEncryptedComment. Contract notifications carrying forwarding
  TON are rejected, preventing amount-collision false credits.

- Defensive guards: empty tx.Hash is skipped (would otherwise
  dedup-collide), bounced messages are filtered, no-active-lock logs
  are debug-level so active wallets don't flood the journal.

Behavior verified end-to-end on TON testnet through several
confirmation cycles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant