Choosing and sizing the TON endpoint
How TON_NETWORK picks the RPC and key pair, what each deposit poll, background scan, balance read and withdrawal costs in Toncenter requests, and the back-off ladder that ends in a stopped monitor.
TON is the chain on this platform most likely to be throttled. Every deposit
poll, every background scan, every balance read and every step of every
withdrawal goes through one HTTP endpoint, and the default that endpoint
resolves to — anonymous toncenter.com — allows roughly one request per
second for the whole install, shared across every user.
Nothing in the TON service paces itself against that. This page is about choosing the endpoint, sizing it, and recognising what running out looks like.
The endpoint pair
TON_NETWORK selects one of two pairs. It is not a general network selector —
it is a two-way switch, and only one value is special.
TON_NETWORK |
RPC variable | Key variable | Default endpoint |
|---|---|---|---|
exactly testnet |
TON_TESTNET_RPC |
TON_TESTNET_RPC_API_KEY |
https://testnet.toncenter.com/api/v2/jsonRPC |
| anything else, including unset | TON_MAINNET_RPC |
TON_MAINNET_RPC_API_KEY |
https://toncenter.com/api/v2/jsonRPC |
The comparison is a literal string equality against "testnet". Testnet,
TESTNET, test, a typo, an empty value and an absent value all select
mainnet, and none of them raises an error or writes a log line.
The requirements console trims TON_NETWORK before comparing it. The TON
service does not. So TON_NETWORK=" testnet" makes the diagnostics probe the
testnet endpoint and report it as healthy, while every real deposit poll and
withdrawal runs against mainnet.
The console also grades the value itself: anything other than mainnet or
testnet is shown as an invalid value even though the runtime silently accepts
it as mainnet. Read that line rather than assuming the blank is a default.
The API key is optional in the code and required in practice
The key is attached by the HTTP provider as an X-API-Key header on every
request. Nothing refuses to start without it. At service construction the
backend logs a single line under the TON tag:
No TON API Key provided. Some functionalities may be limited.That is the only warning you get at runtime. The diagnostics are blunter about it. Run Admin → Ecosystem → Blockchains → Requirements → TON and the live probe names the condition in its own label:
| What the probe shows | Meaning |
|---|---|
Toncenter getMasterchainInfo + API key |
A key is configured and was sent |
Toncenter getMasterchainInfo — NO KEY (anonymous, ~1 req/s) |
No key; the check passes as a warning, not a success |
masterchain seqno <n> |
The endpoint answered and is live |
HTTP 401 — invalid API key |
The key is wrong, not missing |
HTTP 429 — rate limited |
You are already over quota during a single probe |
The report also carries an explicit warning string when the key for the active network is unset:
No TON_MAINNET_RPC_API_KEY — toncenter anonymous rate limits (~1 req/s) will
throttle deposit polling and withdrawal confirmation.What each flow costs
Every number below is a JSON-RPC request against your endpoint. Nothing in the TON service batches or caches them, and the service has no client-side rate limiter of its own — the only pacing anywhere is the background scanner's token bucket and the withdrawal queue's per-chain cooldown.
| Flow | Requests | Cadence |
|---|---|---|
| Live deposit poll, per watched address | 1 getTransactions (10 most recent, archival), plus 1 more for each new transaction it decides to credit |
Every 60 seconds while the user has the deposit page open |
| Background scan pass, per address | Same shape: 1 getTransactions, plus 1 per new transaction |
Every ECOSYSTEM_SCAN_INTERVAL_MS (default 120 s), paced by a 0.5 requests/second bucket for TON |
| Transaction list for a TON address | 1 getTransactions |
Per view. Not cached — the Redis cache in the transaction fetcher covers the EVM path only |
| Master wallet balance | 1 getAddressBalance per TON master wallet |
Every load of the master wallet list or detail screen; uncached, with a 10-second global timeout on the list |
| One withdrawal | 1 runGetMethod (seqno) + 1 getAddressBalance + 1 sendBoc + 1 to 10 getTransactions (5 most recent) |
The confirmation polls are 10 seconds apart, so a slow one occupies the endpoint for about 100 seconds |
A withdrawal therefore costs between four and thirteen requests, and the withdrawal queue enforces a 5-second cooldown between withdrawals on the same chain specifically so a burst does not trip the quota.
Lowering ECOSYSTEM_SCAN_ACTIVE_TTL_MS is the safest lever: it reduces load in
direct proportion to how many idle addresses you stop watching. Raising
ECOSYSTEM_SCAN_RATE_TON is the one that bites, because the quota you are
spending is shared with the live monitors and with every withdrawal in flight.
The failure ladder
Throttling does not announce itself. It arrives as errors inside loops that absorb them, and the loops give up in a particular order.
The live deposit monitor backs off, then stops. It counts consecutive
polling errors. The next poll is scheduled at 60 s × 2^min(errors, 4), so the
interval walks 2, 4, 8 and then 16 minutes and stays there. At ten consecutive
errors the monitor deletes itself and logs:
Too many consecutive errors for <address>, stopping deposit monitoringAdd the delays up and a dead or throttled endpoint gets roughly 110 minutes before that address stops being watched at all. The monitor does not resume on its own — the user has to reopen the deposit page. One success anywhere in the sequence resets the counter and the interval back to 60 seconds.
The background scanner does not stop. A failed pass is logged as a warning and the address is simply rescheduled. This is why deposits often keep crediting, late, on an endpoint that has already killed every live monitor.
Withdrawal confirmation is where it costs money. The transfer is broadcast
before the confirmation polling begins. Ten failed polls and the handler
throws, the row is marked FAILED, and the queue refunds the user — with the
coins already on-chain. That is the expensive failure and it is covered in full
in Withdrawals and fees.
The withdrawal handler asks the wallet contract for its sequence number, and
that call swallows every error — a throttled read, a timed-out read and a
genuinely undeployed contract are indistinguishable. All three come back as
null, which the handler treats as 0.
Seqno 0 is only correct for a wallet that has never sent anything. For an address that has withdrawn before, the message is signed against the wrong sequence number, the wallet contract will not accept it, and the withdrawal then falls into the confirmation-timeout path above.
Replacing toncenter
You are not tied to toncenter.com. The integration uses only the v2 jsonRPC
surface, and only five methods:
| Method | Used by |
|---|---|
getTransactions |
Deposit polling, background scanning, transaction lists, withdrawal confirmation |
getAddressBalance |
Master wallet balances, and the pre-flight balance check on every withdrawal |
runGetMethod |
Reading a wallet contract's sequence number |
sendBoc |
Broadcasting the signed withdrawal |
getMasterchainInfo |
The admin diagnostics probe only |
Anything that speaks that surface at the URL you configure will work —
a paid Toncenter plan, another hosted provider, or your own
ton-http-api in front of your own
liteserver. Point TON_MAINNET_RPC at its /api/v2/jsonRPC path and put its
key, if it has one, in TON_MAINNET_RPC_API_KEY.
Deposit polling requests transactions with the archival flag set. Whatever you point the platform at has to be backed by an archival node, or that call — the one every deposit depends on — will not return the history it is asking for. The withdrawal confirmation poll does not set the flag.
Validate the swap before you trust it:
-
Edit
.envwith the new URL and key. -
Restart both processes — see below.
-
Run the diagnostics. Admin → Ecosystem → Blockchains → Requirements → TON → run the test. You want the RPC check reporting
masterchain seqnowith a latency you are happy with, and noNO KEYlabel. -
Read the readiness rows, not the ticks. Deposits and Withdrawals each get their own row. A healthy endpoint with a locked vault still reports Withdrawals as failed.
-
Move real coins. Deposit a small amount, watch it credit, withdraw a smaller amount back out and confirm the row reaches
COMPLETEDwith a hash.
Changing the endpoint needs a restart — of two processes
The TON service is a per-process singleton that reads TON_NETWORK, the RPC URL
and the API key once, when it is first constructed, and never again.
Two processes construct it. The API host runs the live deposit monitors and signs the withdrawals users submit; the scheduler process runs the background deposit scanner and the pending-withdrawal recovery sweep, which signs too. Restart both:
pm2 restart backend
pm2 restart cronRestarting only backend leaves the scanner talking to the old endpoint, which
produces the confusing shape where live monitoring moves to the new provider and
background detection does not.
| Change | Restart needed |
|---|---|
TON_NETWORK, either RPC URL, either API key |
Yes, both processes |
ECOSYSTEM_SCAN_RATE_TON, ECOSYSTEM_SCAN_INTERVAL_MS, ECOSYSTEM_SCAN_ACTIVE_TTL_MS |
Yes — the scanner reads them at module load |
| Enabling the chain row | No — a service that is currently inactive re-checks the row on its next use |
| Disabling the chain row | Yes, both processes — once the service has gone active the row is never read again |
| Enabling the token row, editing fee or precision | No |
The two directions are not symmetrical. The service caches its active flag and
only re-reads the chain row while that flag is false, so switching TON on is
picked up within one deposit poll, balance read or withdrawal. Switching it
off is not: there is no per-request check anywhere on the deposit or
withdrawal path, so a process that has already seen TON as active keeps polling
deposits, reading balances and signing withdrawals against a chain the admin
screen shows as disabled. Restart backend and cron to make a disable stick.
Related
- Deposit addresses and detection — the two detection paths in detail
- Withdrawals and fees — what the confirmation loop protects
- Wallets, keys and recovery — the key material behind every one of these calls
- Configuration reference — every variable named here
- Troubleshooting — symptom-first version of the failure ladder