Deposit addresses and detection
How TON deposit addresses are derived, why the wallet contract version decides what address a mnemonic produces, why there is no memo to collect, and exactly when a deposit becomes a balance.
Every ECO wallet holding TON gets its own TON account. That account is where the coins actually are — there is no pooling address, no shared contract and no sweep. This page covers how the address is produced, what form it takes, and the path from a transaction landing on-chain to a number changing in the platform.
How an address is derived
The first time a user opens the deposit page for TON, the platform generates a wallet for them:
-
A 24-word TON mnemonic is generated and validated. TON mnemonics are not BIP-39 — they use TON's own scheme, and the platform generates and validates through TON's own library.
-
The mnemonic is converted to an Ed25519 key pair.
-
A wallet contract is created from the public key, and its address is the hash of that contract's initial state.
-
The mnemonic, public key and private key are encrypted with the Ecosystem vault key and written to
wallet_dataas a single blob, alongside the address entry in the wallet's address map.
The encrypted blob is the only persisted copy of that key material. Nothing is written to disk in plaintext, and there is no export path. If the vault key is lost, every TON address on the install becomes unspendable.
The stored address entry also records the network the address was generated
under, taken from TON_NETWORK at the time of generation.
Wallet versions, and why they matter
On TON, an address is derived from the public key and the code of the wallet contract that holds it. The same key produces a different address for every wallet version. The bundled library creates v3R1 wallets by default, and the platform does not override that.
Import a platform-generated mnemonic into a modern mobile wallet and it will show you a v4R2 or W5 address with a zero balance. The funds are not missing — they are at the v3R1 address, which that wallet is not showing you. Any manual recovery has to select the v3R1 wallet version explicitly.
This also explains the shape of the withdrawal path. When the platform signs a withdrawal it rebuilds the wallet from the stored key pair and the stored address, so the two can never drift apart. What it does not do is search other versions for funds, so anything sent to a v4 address derived from the same key is invisible to the platform.
Address format
The address handed to users is the user-friendly, non-bounceable form: 48
characters, beginning UQ on mainnet.
Non-bounceable is deliberate and it is the right choice for a deposit address. A TON account derived from a key does not exist on-chain until something deploys its contract, which for these wallets happens on their first outgoing transaction. A transfer sent to an uninitialised address with the bounceable flag set is returned to the sender; sent non-bounceable, it simply arrives and sits there. Users depositing to a brand-new platform address rely on that.
Two format details catch people out.
The address is emitted in standard base64, not URL-safe base64. That means
it can contain + and / characters. It is a completely valid TON address and
every wallet accepts it, but it is not the -/_ form most block explorers
display. The two decode to the same account.
On a testnet install the address still carries the mainnet form. The
test-only flag is not set when the address is rendered, so you get UQ… rather
than the 0Q… you may expect. The underlying account is identical; only the
display hint differs.
The withdrawal endpoint's character gate accepts letters, digits, -, _ and
: only. Paste a standard-base64 TON address into it and you get
400 — Invalid address format. Address contains invalid characters or suspicious patterns. before any TON library sees the string.
The fix is a straight character swap: replace + with - and / with _. The
result is the same address in URL-safe form and is accepted.
There is no memo, tag or comment
Deposits are attributed by destination address. The deposit parser reads the sender, the destination and the value; it never looks at the message body.
So: users sending from an exchange that offers a "memo", "comment" or "destination tag" field should leave it blank. Filling it in is harmless — it is ignored — but instructing users to enter one is not, because it implies a shared deposit address that this platform does not use.
If you have written support macros for other exchanges that require a comment on TON deposits, they do not apply here.
How a deposit is detected
Three things can find a TON deposit, and all of them do the same work.
The session monitor. While the user has the deposit page open, a WebSocket monitor polls their address once a minute. Each poll fetches the address's most recent 10 transactions.
The background scanner. Every address shown on a deposit page is registered in a Redis working set for 72 hours and re-scanned roughly every two minutes by a single-instance background loop. This is what catches "user sent funds then closed the tab". TON is rate-limited to 0.5 scans per second across the whole install, sized for anonymous Toncenter.
A page revisit. Opening the deposit page again restarts the session monitor and refreshes the 72-hour window.
For each transaction found, the platform:
- skips it if a transaction row already carries that hash, or if the same hash was processed within the last 30 minutes;
- skips it unless the transaction succeeded;
- re-reads the transaction, converts both the expected and actual destination to
the raw
workchain:hexform and compares them, so the address format cannot cause a mismatch; - records a
NATIVEDEPOSITfor the wallet with the value converted from nanotons.
TON deposits do not wait for confirmations
The confirmation watchdog that counts block depth for Bitcoin and the EVM chains treats TON — along with Solana, Tron and Monero — as already settled: if the recorded status is complete, it is confirmed. TON deposits are written as completed at detection.
That is a reasonable model for TON's finality, but be clear about what it means
operationally: there is no confirmation counter to watch, the parsed transaction
carries N/A for both confirmations and fee, and the credit happens on the
first poll that sees the transaction rather than some minutes later.
Failure behaviour worth knowing
The monitor gives up. Ten consecutive polling errors and the session monitor stops for that address and logs it. Before that, it backs off — the interval doubles with each consecutive error up to sixteen times the base, so a dead endpoint is retried every sixteen minutes rather than every minute. It does not resume on its own; the user reopening the deposit page starts a fresh session.
Only the last ten transactions are visible per poll. An address that receives more than ten transactions between two polls loses the oldest ones from the window. In normal use this never happens — a per-user address receiving ten deposits in sixty seconds is not a real pattern — but it is the reason a bulk test script firing many small sends can appear to lose some.
A deposit is credited once. Deduplication is on the transaction hash, in the database, so the session monitor and the background scanner finding the same transaction cannot double-credit.
Related
- Withdrawals and fees — the other half of custody
- Configuration reference — every variable read
- Deposit wallets and custody — the Ecosystem machinery this plugs into