TON is not Ethereum with a different RPC. The asynchronous transaction model and tree-like message structure break developer intuition. When a user sends native TON to your address, it's a single transaction. When they send Jetton (USDT on TON), it's a chain of three: transfer → internal message → notification. A monitoring error leads to lost payments and headache with refunds.
Recently, a project with 5000 daily Jetton payments approached us. After auditing their system, we found they weren't accounting for bounced transactions, causing 2% of payments to be credited erroneously. We rebuilt the architecture with unique addresses and Gasless relays, cutting losses to zero. We set up payment acceptance turnkey: contact us, and we'll assess your project and choose the optimal architecture in one day.
How to Accept Native TON and Jetton
Native TON
Generate a unique address or use a single address with a comment (memo) for identification. Monitor via TON Center API or TonAPI:
import { TonClient } from '@ton/ton';
import { Address } from '@ton/core';
const client = new TonClient({
endpoint: 'https://toncenter.com/api/v2/jsonRPC',
apiKey: process.env.TONCENTER_API_KEY,
});
async function checkIncomingTransactions(
address: string,
lastLt: string // last known logical time
) {
const addr = Address.parse(address);
const transactions = await client.getTransactions(addr, {
limit: 20,
lt: lastLt,
archival: false,
});
for (const tx of transactions) {
// Only incoming, not bounce
if (tx.inMessage && tx.inMessage.info.type === 'internal') {
const info = tx.inMessage.info;
const value = info.value.coins; // in nanoTON
const comment = tx.inMessage.body; // text comment
// Match comment with our payment ID
console.log(`Received: ${value} nanoTON, comment: ${comment}`);
}
}
}
Important: check the bounce flag and bounced flag. A bounced transaction means a return—do not count it.
Jetton (USDT, USDC, NOT)
Jetton Transfer is more complex: the user sends a message to their JettonWallet, which sends an internal message to the recipient's contract, which then sends a transfer_notification to the recipient's address. In forward_ton_amount, we include the fee for the notification; in forward_payload, we include the payment ID:
transfer_notification#7362d09c
query_id: uint64
amount: coins // amount of Jetton
sender: MsgAddress // sender's address
forward_payload: ^Cell // our custom payload (payment ID)
Monitor not the main address, but the JettonWallet of our address:
// Get our JettonWallet address for USDT
async function getJettonWalletAddress(
ownerAddress: string,
jettonMasterAddress: string
): Promise<string> {
const master = client.open(
JettonMaster.create(Address.parse(jettonMasterAddress))
);
const walletAddr = await master.getWalletAddress(
Address.parse(ownerAddress)
);
return walletAddr.toString();
}
// USDT on TON mainnet
const USDT_MASTER = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs';
What to Choose: Unique Addresses or Comment?
| Characteristic |
Comment (memo) |
Unique Address |
| Implementation complexity |
Low |
Medium (HD wallet) |
| User errors |
1-3% forget comment |
0% |
| Fund sweeping |
Not required |
Required sweep |
| Monitoring |
One address |
Many addresses |
| Recommendation |
Up to 100 payments/day |
From 1000 payments/day |
Comment/Memo Identification
One address, user specifies comment (payment ID). Simple, but requires UX—explain the need for a comment. Error = lost payment (needs manual reconciliation).
Unique Address per Payment
Generate HD wallet (BIP39 + non-standard derivation). Each order gets a separate address. No comments, no errors, simple monitoring:
import { mnemonicToPrivateKey } from '@ton/crypto';
import { WalletContractV4 } from '@ton/ton';
async function derivePaymentAddress(
masterMnemonic: string[],
orderIndex: number
): Promise<string> {
const keyPair = await mnemonicToPrivateKey(masterMnemonic);
const wallet = WalletContractV4.create({
publicKey: keyPair.publicKey,
workchain: 0,
walletId: 698983191 + orderIndex, // unique subwalletId
});
return wallet.address.toString({ bounceable: false });
}
Downside: need to sweep funds to a main address.
Polling or Webhook?
| Method |
Latency |
Load |
Complexity |
| Polling (TON Center) |
~5-30 sec |
Medium |
Low |
| Webhook (TON Center) |
~1-2 sec |
Low |
Medium |
| WebSocket (TonAPI) |
~0.5 sec |
Low |
High |
| Own node |
~0 sec |
Very high |
Very high |
For production, use TonAPI + WebSocket with polling fallback. A self-hosted node is justified for millions of transactions per day. Webhook is 10x faster than polling.
Gasless and Bounce: Common Problems
Gasless
Gasless allows users to pay without a TON balance for fees. This is critical for Jetton payments: to send USDT, you need TON for gas. The service covers the fee via a relay. Set up a relay via TON Connect or a relay contract. Gasless increases conversion by 15-30% in mobile apps.
Bounce
If you don't filter bounced transactions, you may credit a payment that never arrived. In TON, bounces are normal: the recipient contract may reject the message. Check the bounced flag in the message body. For Jetton, also track transfer_notification—its absence is also a sign of failure.
Steps to Set Up TON Payment Acceptance
-
Analysis—assess load, asset types (TON, Jetton), choose architecture.
-
Choose identification method—comment or unique addresses.
-
Develop monitoring—integrate with TON Center / TonAPI, handle webhook/WebSocket.
-
Integrate with backend—map transactions to orders, handle errors.
-
Test on testnet—use a bot to distribute test TON and Sandbox from Blueprint.
-
Deploy—set up production environment, monitoring, and alerts.
What's Included in the Work
-
Architecture documentation—detailed design of payment flow.
-
Access to test environment—testnet endpoints and credentials.
-
Monitoring setup—alerts for bounced transactions and failures.
-
Training session—one-hour walkthrough for your team.
-
Support for 30 days after deployment.
Our Company Metrics
-
5+ years in blockchain development.
-
50+ projects delivered, including 10+ TON integrations.
-
99.9% uptime for payment systems.
Timelines and Cost
Basic setup takes 2–4 weeks. Costs start from $5,000 for a simple setup with one asset and comment-based identification. Gasless relay and multi-asset support increase the budget. Get a free project assessment—contact us today.
Common Mistakes in TON Payment Acceptance
- Ignoring bounced transactions—crediting failed payments.
- Monitoring the main address instead of JettonWallet—missing Jetton payments.
- Using only polling without fallback—losing transactions under high load.
- Not testing on testnet—production errors.
- Not accounting for asynchronicity—trying to wait synchronously for a contract response.
Testing and Deployment
Testnet: use a bot to distribute test TON. API endpoint https://testnet.toncenter.com/api/v2/jsonRPC. For local development, use Sandbox from Blueprint: a TVM emulator without network. The asynchronous message model requires special testing. Order TON payment acceptance setup to eliminate monitoring errors and automate accounting.
References: TON Documentation, TON Center API, TonAPI, Blueprint Sandbox
Blockchain Infrastructure Deployment: Nodes, RPC, Indexing
Subgraph fell at 3:47 AM. By morning users saw outdated balances, transactions "hung" in the UI, support received 47 tickets in an hour. Cause: the handler in the subgraph failed on a transaction with a non-standard event log — and the entire index stopped. We have encountered such situations dozens of times. Our experience shows: blockchain infrastructure does not forgive gaps in observability. Guaranteeing uptime without multi-layered monitoring and fault-tolerant architecture is impossible. Over 8 years working with Ethereum, Polygon, and Solana, we have developed an approach that allows predictable deployment of infrastructure of any scale — from a single node to a multichain grid with dozens of subgraphs.
RPC Layer Architecture
Every dApp interaction with the blockchain goes through RPC — the JSON-RPC API provided by a node. Three options:
Managed providers — Alchemy, QuickNode, Infura, Ankr. Minimal operational costs, SLA, built-in monitoring. Limits: rate limits (Alchemy Free: 300 RU/sec), vendor lock, potential downtime during provider incidents. For most projects — the right choice at the start.
Self-owned nodes — full control, no rate limits, no third-party dependence. Cost: archive Ethereum node requires 2.5–3TB SSD, a strong server, and DevOps support. Sync from scratch on Ethereum via Geth/Nethermind — 3–7 days. Justified under high load or latency requirements.
Hybrid — self-owned node as primary, managed provider as fallback. Standard for protocols with high TVL. Proper load balancing can reduce costs by 20–30% compared to pure managed setup. Under high monthly request volume, hybrid saves significantly.
| Provider |
Strength |
Limitation |
| Alchemy |
Supernode, Enhanced APIs, webhooks |
Expensive on high-volume |
| QuickNode |
Low latency, multi-chain |
More expensive than Alchemy on basic plan |
| Infura |
Historical reliability |
Rate limits on free, one major incident halted half of DeFi |
| Ankr |
Cheap, 40+ chains |
Less stable |
How to Set Up an RPC Layer Without a Single Point of Failure?
At least two providers, DNS round-robin with health check every 5 seconds, automatic fallback when latency >500 ms. In practice, this gives 99.99% availability during any provider failure. For protocols with high TVL, we recommend a custom HA-proxy (nginx or Envoy) in front of two managed providers.
Why Is a Hybrid RPC Scheme More Cost-Effective Than Pure Managed?
At high request volumes, managed providers can be very expensive; a hybrid using a self-owned node as primary and a managed fallback cuts costs significantly without losing SLA.
Ethereum Node Clients
Execution clients: Geth (most used), Nethermind (C#, fast sync), Besu (Java, enterprise), Erigon (fastest sync, efficient archive mode ~2TB instead of 3TB).
Consensus clients (post-Merge): Lighthouse (Rust), Prysm (Go), Teku (Java), Nimbus (Nim). Each node after The Merge requires a pair of execution + consensus clients.
For DevOps: eth-docker — Docker Compose configurations for all client combinations. Setting up monitoring via Grafana + Prometheus is mandatory; a standard dashboard is available in each client's repository.
The Graph: Event Indexing
The Graph Protocol — decentralized indexing. A subgraph describes which events from which contracts to index and how to transform them into a GraphQL schema.
Subgraph structure:
-
subgraph.yaml — manifest: contract addresses, startBlock, events to handle
-
schema.graphql — GraphQL schema of entities
-
src/mapping.ts — AssemblyScript event handlers
dataSources:
- kind: ethereum
name: UniswapV3Pool
network: mainnet
source:
address: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"
abi: UniswapV3Pool
startBlock: 12370624
mapping:
eventHandlers:
- event: Swap(indexed address,indexed address,int256,int256,uint160,uint128,int24)
handler: handleSwap
AssemblyScript handlers — not TypeScript. No nullable types, no closures, no many standard APIs. An error in the handler stops the subgraph indexing on that transaction. Important: add try-catch for operations that can fail (e.g., store.get() for an entity that may not exist).
How to Avoid Subgraph Indexing Stops?
Graph Node logs are monitored in real-time; on hasIndexingErrors = true an alert fires and an automatic node restart (via systemd or Kubernetes). Typical downtime on error — 150–300 seconds to recover. Additionally, for production we set up a watchdog that restarts Graph Node if subgraph lag exceeds 50 blocks.
Choosing Between Hosted Service and Decentralized Network
Graph Hosted Service (free, centralized) is deprecated in favor of Subgraph Studio + Graph Network. For production: deploy on Graph Network with GRT curation signal — the subgraph gets indexers proportional to curation.
Alternatives to The Graph: Ponder (TypeScript, self-hosted, easier to debug), Envio (ultra-fast indexer, supports EVM + non-EVM), Subsquid (TypeScript, own network), Moralis Streams (managed, webhook-based). Our experience shows: for high-load projects with unique logic, Ponder or Envio are more effective — they give full control over the process and do not require GRT tokenomics.
Webhooks and Real-Time Notifications
Alchemy Webhooks and QuickNode Streams allow receiving events in real-time via HTTP webhook or WebSocket. For monitoring addresses, new transactions, mints — this is faster than polling RPC.
Tenderly — platform for monitoring and alerts. You can set up an alert for a specific contract event, balance change, function call with certain parameters. Transaction simulation via Tenderly API is invaluable for debugging.
Monitoring and Observability
Minimum monitoring stack for a protocol:
On-chain: OpenZeppelin Defender Sentinel — watches contract events, triggers webhook or Autotask when conditions are met. Forta Network — community-maintained bots detect anomalies (large withdrawals, flash loans, governance attacks).
Infrastructure: Grafana + Prometheus for nodes, Datadog or Grafana Cloud for managed metrics. Alerts on: node is 10+ blocks behind, RPC latency >500ms, subgraph lag >100 blocks.
Uptime: Better Uptime or PagerDuty on RPC endpoint and subgraph health endpoint (The Graph provides _meta { hasIndexingErrors, block { number } }).
Why Is Monitoring Without Tenderly Insufficient?
Tenderly provides transaction simulation and detailed traces — critical for debugging subgraph and smart contract errors. Forta focuses on network anomalies, not your infrastructure. The combination of Tenderly plus a custom Grafana dashboard covers 90% of incident scenarios.
Multichain Infrastructure
A protocol on 5 chains = 5 separate RPC endpoints, 5 subgraphs, 5 monitoring configs. Manageable but requires deployment automation.
For subgraph multi-network deployment: graph deploy --network mainnet, graph deploy --network arbitrum-one etc. with a unified codebase and network-specific addresses in separate config files.
Chainlink CCIP and LayerZero for cross-chain messaging require monitoring of both chains and transactions on intermediate relayers. A reorg on the source chain after a confirmed mint on the target chain is a classic bridge problem. Solution: wait for finality (on Ethereum ~15 minutes after Merge for economic finality) before confirming on the target chain.
Infrastructure Setup Process
- Audit current stack — determine chains, request volume, latency and availability requirements.
- Architecture design — select providers, load balancing, redundancy.
- Subgraph development — manifest → schema → handlers → testing on local Graph Node → deploy to testnet → mainnet.
- Monitoring configuration — Tenderly alerts, Grafana dashboard, PagerDuty integration.
- Documentation and runbook — what to do when: subgraph falls behind, RPC downtime, node desync.
- Handover to operations — team training, access transfer, first month support.
What's Included
- Deployment of managed or self-hosted Ethereum, Polygon, BNB Chain nodes
- RPC layer setup with primary/fallback and load balancing
- Subgraph development and deployment for your protocol
- Monitoring connection (Tenderly, Grafana, alerts)
- Runbook and operations documentation
- Team training (up to 4 hours online)
- 30-day support after delivery
Timeline
| Task |
Duration |
| RPC and basic monitoring setup |
1–2 weeks |
| Subgraph for one protocol |
2–4 weeks |
| Self-hosted node with monitoring |
2–3 weeks |
| Full infrastructure (multi-chain, monitoring, runbooks) |
6–10 weeks |
All projects are managed in a GitHub/GitLab repository with CI/CD; configuration code stays with you. Order infrastructure deployment — we'll show how to cut costs by 20–30% without losing reliability. Get a consultation — we'll demonstrate how we deployed infrastructure for a protocol with large TVL on Ethereum and Arbitrum. Contact us.