All posts
9 min read

TON Address Formats: EQ, UQ, Raw — and Why Deposits Bounce

EQ, UQ and raw TON address formats, bounceable vs non-bounceable, and why the first deposit to a fresh wallet bounces straight back to the sender.

TONTON addressesbounceableEQ UQ rawparse_addressdeposit

A developer spins up a fresh wallet, copies the address, sends the first 5 GRAM over for gas — and a minute later the coins are back in the sending wallet. Balance on the new address: zero. No error, no "reverted", the transaction went through just fine. The money simply… bounced. It's a classic in TON chats: "sent to my own address — it never arrived." And almost every time the cause is the same — mixed-up address formats and the bounceable flag.

Let's break it down fact by fact: what TON address formats — EQ, UQ and raw actually are, how bounceable differs from non-bounceable, and why the first deposit to a new wallet goes right back to the sender. Along the way I'll show how to verify all of this with a single tool call from an AI agent, without touching an SDK or running a node.

The three TON address formats: raw, EQ and UQ — what's the difference

Under the hood, every TON address is the same thing: a workchain number plus the 256-bit hash of the contract's state init. That's the raw format:

0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8

Left of the colon is the workchain (usually 0 for the basechain, -1 for the masterchain), right of it is the 256-bit hash in hex. It's precise and unambiguous, but it carries no checksum: get one character wrong and you end up with a different address that still looks perfectly valid. Raw is what contract get-methods and low-level tooling typically expect — humans almost never see it.

The second representation is the user-friendly one: those 48 base64url characters you see in wallets and explorers:

EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N     (bounceable)
UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI     (non-bounceable)

It encodes the same "workchain + hash" pair, plus a flags byte and a CRC16 checksum at the end. The CRC catches typos: a corrupted address simply fails validation before anything gets sent. And it's that flags byte that produces the EQ and UQ prefixes.

The key thing to get straight right away:

  • raw and user-friendly are two ways of writing the same address.
  • EQ and UQ are two variants of one user-friendly address, differing by exactly one flag.

In other words, EQ… and UQ… for the same wallet point to the same raw hash, the same contract, the same balance. They are not two different wallets or two different accounts. The only difference is one bit expressing the sender's intent.

Bounceable (EQ) vs non-bounceable (UQ): what the flag means

That flags byte inside the user-friendly address is what determines the prefix:

Flag Tag Prefix (mainnet) Prefix (testnet)
bounceable 0x11 EQ kQ
non-bounceable 0x51 UQ 0Q

For testnet, 0x80 gets added to the tag — hence the exotic kQ and 0Q. But what matters is what the flag means, not the tag arithmetic.

Bounceable (EQ) literally means: "if anything goes wrong on the receiving end — send the coins back to me." It's a safety net for smart contracts. If you're sending money to a contract that's supposed to process it, and that contract throws an error, isn't deployed yet, or runs out of gas — you don't want the coins stranded in the void. The bounce mechanism returns them to the sender, minus fees.

Non-bounceable (UQ) means the opposite: "deliver and leave it there, no matter what." No returns — the coins just land on the address.

For everyday transfers between people the difference is invisible — until one very specific case.

Why the first deposit to an undeployed wallet bounces

Here's the trap. In TON, a wallet address exists before the wallet contract is actually deployed to the blockchain. The address is a deterministic hash of the contract code and its initial data (the public key), so you know it the moment you generate the mnemonic, fully offline. But until the first transaction touches that address, the account sits in the uninit (uninitialized) state: the address exists, but there's no contract code on it.

Now watch what happens with a bounceable transfer:

  1. You send a transfer to this uninit address in the EQ (bounceable) format.
  2. The network tries to deliver the coins and "invoke" the receiving contract. There's nobody home — no contract at the address.
  3. Bounce kicks in: delivery failed, so the coins go back to the sender, minus fees.

The result is exactly that "bounce". The transaction succeeded, but the new wallet's balance stayed at zero and the money went back where it came from. Nobody stole anything, the network did precisely what it was designed to do — you just used the bounceable format where you shouldn't have.

And if you send the same transfer in the UQ (non-bounceable) format?

  1. The network tries to deliver the coins to the uninit address.
  2. Bounce is disabled by the flag — nothing needs to be returned.
  3. The coins stay on the address, even though the contract isn't deployed yet.

The wallet is funded. Later, when you send your first outgoing transaction from it, the contract code gets deployed along with it — and the account becomes active. From that point on it happily accepts any transfer, bounceable included. The UQ rule is critical only for the very first top-up of a not-yet-deployed wallet.

The good news: the ecosystem is gradually closing this gap on the user's behalf. v5r1 wallets and a number of clients show the address in non-bounceable (UQ) form by default — precisely so a newcomer doesn't lose their first deposit. But the moment you handle addresses programmatically — from a backend, a script, an AI agent — the flag is your responsibility again.

How to send the first transfer correctly: UQ instead of EQ

The practical rule fits in one line:

The first deposit to a new (uninit) wallet always goes to UQ. After that, anything goes.

The expanded logic for any service that sends funds to users:

  • Recipient address is in the uninit state → send to UQ (non-bounceable).
  • Address is activeEQ (bounceable) is fine.
  • Sending to a smart contract (a DEX, a jetton minter, an escrow) → EQ, so the money comes back on failure.

The problem is that to the naked eye EQ and UQ differ by a single prefix character, and the uninit/active state isn't visible from the address at all. Both checks have to be done programmatically. And here's the thing — you don't need to drag @ton/ton into the project, wire up a provider and parse cells. Both questions are covered by two tools on an MCP server that the AI agent calls on its own.

If wallet generation and that first top-up are new territory for you, there's a dedicated walkthrough in the guide to creating a TON wallet.

parse_address: convert and validate addresses offline in one call

parse_address is an offline tool from TONNode, a hosted MCP server for TON. It decodes an address, recomputes the formats and verifies the CRC16 without touching the network: it needs no node and no key — it's pure math on a string, so it's instant and burns none of your rate limit.

What it can do:

  • convert EQ ⇄ UQ ⇄ raw in any direction;
  • validate an address — check whether the checksum adds up;
  • show the bounceable flag and the workchain number.

MCP (Model Context Protocol) is the standard that AI agents (Claude, Cursor, ChatGPT/Codex and any MCP client) use to call tools. Hooking up an MCP server takes one entry in the client config. The free local option with the full set of read tools:

{
  "mcpServers": {
    "ton": {
      "command": "npx",
      "args": ["-y", "@tonnode/mcp"]
    }
  }
}

From there, a plain prompt to the agent is enough:

Take the address EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N and show it in non-bounceable (UQ) and raw formats. Verify that the checksum is valid.

The agent calls parse_address and returns the UQ version of the same wallet, the raw hash and the validity status. No manual base64url juggling, no risk of fat-fingering one of 48 characters.

get_account_state: check whether the wallet is deployed before sending

The format isn't the whole story — you also need to know what state the address is in: active or uninit. That's an on-chain question, and get_account_state answers it. The tool returns the account status, flags and data about the last transaction.

The logic before sending the first transfer:

  • get_account_state returned uninit → the address isn't deployed yet → send to UQ.
  • Returned active → the wallet is deployed → sending to EQ is safe.

Prompt for the agent:

Use get_account_state to check whether the address UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI is deployed. If the state is uninit, remind me that the first deposit must go to the UQ format.

The full pipeline looks like this: generate_wallet creates a wallet (versions v3r2/v4/v5r1/highload_v3) and hands you an address that at creation time is guaranteed to be uninit — the network has never heard of it. Which means the first deposit to it goes strictly to UQ. Before sending, get_account_state confirms the address really is uninit, and parse_address guarantees you grabbed the non-bounceable representation. After the top-up, it's worth calling get_balance to confirm the coins landed on the address instead of bouncing.

One important note about generation: generate_wallet returns the mnemonic, keys and address to the user — the server doesn't store them and never signs anything on your behalf. That's non-custodial by design, and it applies to all of the swap, cross-chain and wallet tools.

A small terminology note: GRAM is the renamed Toncoin (renamed in June 2026); the network itself is still called TON.

Checklist: how not to lose your first deposit on TON

A short algorithm for every first transfer to a new wallet:

  1. Generated an address (generate_wallet or your own SDK) — assume it's uninit by default.
  2. Check the state via get_account_state: uninit or active.
  3. If uninit — convert the recipient address to UQ with parse_address and send the first deposit to that, and only that.
  4. If activeEQ is fine; for contracts it's actually preferable.
  5. To smart contracts, always send bounceable (EQ), so the money comes back on failure.
  6. After the top-up, check get_balance — the coins should land on the address, not bounce.
  7. Remember: EQ and UQ are the same wallet (one raw hash); you're not choosing an address, you're choosing what happens on failed delivery.

Both key checks — parse_address (offline) and get_account_state (on-chain) — are available out of the box, with no infrastructure of your own. Grab a free Hobby key (60 requests/min, no card required) and call both tools straight from your AI agent: https://tonnode.io/dashboard?plan=hobby.

From there, the same setup makes quick work of the adjacent problems: catching incoming USDT payments on TON, reading a USDT balance in one call or calling a contract get-method without an SDK. Address formats are the foundation everything else stands on: sort out EQ/UQ once, and a bounced deposit will never catch you off guard again.

Give your agent TON access

16 MCP tools: reads, non-custodial swaps, cross-chain and wallets. Free tier — 60 req/min, no card required.