EQ, UQ và raw: định dạng địa chỉ TON, vì sao tiền nạp bật ngược
EQ, UQ và raw: các định dạng địa chỉ TON, bounceable so với non-bounceable và vì sao khoản nạp đầu tiên vào ví mới bị bật ngược về người gửi.
Một dev tạo ví mới, copy địa chỉ, gửi vào đó 5 GRAM đầu tiên để làm gas — một phút sau số coin quay ngược về ví gửi. Số dư của địa chỉ mới: bằng không. Không lỗi, không "reverted", giao dịch chạy thành công. Chỉ là tiền… bật ngược lại. Trong các chat TON đây là chuyện kinh điển: "gửi vào chính địa chỉ của mình mà không tới". Và gần như lúc nào lý do cũng chỉ có một — nhầm định dạng địa chỉ và cái cờ bounceable.
Cùng làm rõ từng điểm một: các định dạng địa chỉ TON — EQ, UQ và raw là gì, bounceable khác non-bounceable ở chỗ nào, và vì sao khoản nạp đầu tiên vào một ví mới lại quay về người gửi. Nhân tiện tôi sẽ chỉ luôn cách kiểm tra tất cả những thứ đó bằng một lời gọi tool từ AI agent, không cần dựng SDK cũng chẳng cần node.
Ba định dạng địa chỉ TON: raw, EQ và UQ — khác nhau ở đâu
Mọi địa chỉ trong TON, bên dưới lớp vỏ, đều là cùng một thứ: số hiệu workchain cộng với hash 256-bit lấy từ state init của contract. Đó chính là định dạng raw:
0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8
Bên trái dấu hai chấm là workchain (thường là 0 — basechain, -1 — masterchain), bên phải là hash 256-bit dạng hex. Định dạng này rõ ràng và không nhập nhằng, nhưng nó không có checksum: gõ sai một ký tự là bạn có ngay một địa chỉ khác trông vẫn hợp lệ. Raw chính là thứ mà các get-method của contract và các công cụ tầng thấp thường yêu cầu, còn người dùng thì gần như không bao giờ nhìn thấy nó.
Cách biểu diễn thứ hai là user-friendly: đúng 48 ký tự base64url mà bạn vẫn thấy trong ví và explorer:
EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N (bounceable)
UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBI (non-bounceable)
Nó nhúng sẵn đúng cặp "workchain + hash" nói trên, cộng thêm một byte cờ và checksum CRC16 ở cuối. CRC bắt lỗi gõ nhầm: địa chỉ bị hỏng sẽ không qua nổi bước validate, ngay từ trước khi gửi. Và cũng chính từ byte cờ đó mà sinh ra tiền tố EQ và UQ.
Điều mấu chốt cần nắm ngay:
- raw và user-friendly là hai cách viết của cùng một địa chỉ.
- EQ và UQ là hai biến thể của cùng một địa chỉ user-friendly, khác nhau đúng một cái cờ.
Nghĩa là EQ… và UQ… của cùng một ví trỏ tới cùng một raw hash, cùng một contract, cùng một số dư. Đây không phải hai ví khác nhau, cũng không phải hai tài khoản khác nhau. Khác biệt chỉ nằm ở một bit thể hiện ý định của người gửi.
Bounceable (EQ) so với non-bounceable (UQ): cái cờ đó nghĩa là gì
Chính cái byte cờ bên trong địa chỉ user-friendly quyết định tiền tố:
| Cờ | Tag | Tiền tố (mainnet) | Tiền tố (testnet) |
|---|---|---|---|
| bounceable | 0x11 |
EQ |
kQ |
| non-bounceable | 0x51 |
UQ |
0Q |
Với testnet thì tag được cộng thêm 0x80 — từ đó mới ra mấy cái kQ và 0Q lạ mắt. Nhưng thứ ta quan tâm là ý nghĩa của cờ, chứ không phải phép tính trên tag.
Bounceable (EQ) nghĩa đen là: "nếu bên nhận có chuyện gì trục trặc thì trả coin lại cho tôi". Đây là cơ chế bảo vệ dành cho smart contract. Nếu bạn gửi tiền cho một contract lẽ ra phải xử lý số tiền đó, mà nó lại lỗi, chưa được deploy hoặc thiếu gas, bạn chắc chắn không muốn số coin đó treo lơ lửng trong hư không. Cơ chế bounce sẽ trả tiền về cho người gửi, sau khi trừ phí.
Non-bounceable (UQ) thì ngược lại: "cứ giao và để đó, có chuyện gì cũng mặc". Không hoàn trả gì cả — coin đơn giản là nằm lại trên địa chỉ.
Với các khoản chuyển tiền hằng ngày giữa người với người, khác biệt này gần như không thấy được — cho tới đúng một tình huống cụ thể.
Vì sao khoản nạp đầu tiên vào ví chưa deploy lại "bật ngược"
Đây chính là cái bẫy. Trong TON, địa chỉ ví tồn tại trước cả khi contract ví thực sự được deploy lên blockchain. Địa chỉ là một hash tất định suy ra từ code contract và dữ liệu khởi tạo (khóa công khai), nên bạn biết nó ngay sau khi sinh mnemonic, hoàn toàn offline. Nhưng chừng nào chưa có giao dịch đầu tiên đi qua địa chỉ đó, tài khoản vẫn ở trạng thái uninit (chưa khởi tạo): địa chỉ thì có, còn code contract trên đó thì không.
Giờ hãy xem chuyện gì xảy ra với một khoản chuyển bounceable:
- Bạn gửi tới địa chỉ uninit này một khoản chuyển ở định dạng EQ (bounceable).
- Mạng lưới cố giao số coin và "gọi" contract bên nhận. Chẳng có ai nhận cả — trên địa chỉ đó làm gì có contract.
- Bounce kích hoạt: đã không giao được thì coin quay về người gửi, sau khi trừ phí.
Kết quả là đúng cái "cú bật ngược" đó. Giao dịch chạy thành công, nhưng số dư của ví mới vẫn bằng không, còn tiền thì trở lại đúng chỗ nó xuất phát. Không ai ăn cắp gì, mạng lưới chạy đúng như thiết kế — chỉ là bạn đã dùng định dạng bounceable ở chỗ lẽ ra không nên dùng.
Còn nếu gửi đúng khoản chuyển đó ở định dạng UQ (non-bounceable) thì sao?
- Mạng lưới cố giao coin tới địa chỉ uninit.
- Bounce đã bị cờ tắt đi — không cần trả lại gì.
- Coin nằm lại trên địa chỉ, bất kể contract vẫn chưa được deploy.
Ví đã được nạp. Về sau, khi bạn gửi giao dịch đi ra đầu tiên từ ví này, code contract sẽ được deploy cùng với nó — và tài khoản chuyển sang trạng thái active. Từ thời điểm đó nó thoải mái nhận mọi khoản chuyển, kể cả bounceable. Quy tắc UQ chỉ thực sự quan trọng ở đúng lần nạp đầu tiên vào một ví chưa deploy.
Tin tốt: hệ sinh thái đang dần bịt cái lỗ này thay cho người dùng. Ví v5r1 và một loạt client mặc định hiển thị địa chỉ ở dạng non-bounceable (UQ) — chính là để người mới không mất khoản nạp đầu tiên. Nhưng ngay khi bạn làm việc với địa chỉ bằng code — từ backend, từ script, từ AI agent — thì trách nhiệm với cái cờ đó lại thuộc về bạn.
Gửi khoản chuyển đầu tiên cho đúng: UQ thay vì EQ
Quy tắc thực dụng gói gọn trong một dòng:
Khoản nạp đầu tiên vào ví mới (uninit) — luôn gửi vào UQ. Sau đó thì thế nào cũng được.
Logic đầy đủ cho bất kỳ dịch vụ nào có gửi tiền cho người dùng:
- Địa chỉ người nhận đang ở trạng thái uninit → gửi vào UQ (non-bounceable).
- Địa chỉ active → gửi vào EQ (bounceable) cũng được.
- Gửi tới smart contract (DEX, jetton minter, escrow) → dùng EQ, để khi lỗi thì tiền quay về.
Vấn đề là nhìn bằng mắt thì EQ với UQ chỉ khác nhau đúng một ký tự tiền tố, còn trạng thái uninit/active thì nhìn vào địa chỉ chẳng thấy gì hết. Cả hai phép kiểm tra đều phải làm bằng code. Và ở đây bạn không nhất thiết phải kéo @ton/ton vào dự án, dựng provider rồi tự parse cell — cả hai câu hỏi đều được giải quyết bằng hai tool của MCP server, do chính AI agent tự gọi.
Nếu chủ đề tạo ví và nạp tiền lần đầu còn mới với bạn, có một bài riêng trong hướng dẫn tạo ví TON.
parse_address: đổi định dạng và kiểm tra địa chỉ offline trong một lời gọi
parse_address là một tool offline của TONNode, MCP server hosted dành cho TON. Nó giải mã địa chỉ, tính lại các định dạng và kiểm tra CRC16 mà không hề chạm tới mạng lưới: nó chẳng cần node lẫn key — đây thuần túy là toán trên một chuỗi ký tự, nên chạy tức thì và không tốn quota.
Nó làm được gì:
- chuyển đổi
EQ ⇄ UQ ⇄ rawtheo mọi chiều; - kiểm tra địa chỉ có hợp lệ không (checksum có khớp hay không);
- cho biết cờ bounceable và số hiệu workchain.
MCP (Model Context Protocol) là chuẩn để AI agent (Claude, Cursor, ChatGPT/Codex và bất kỳ MCP client nào) gọi tool. Kết nối MCP server chỉ cần đúng một mục trong config của client. Bản local miễn phí với đầy đủ bộ tool đọc:
{
"mcpServers": {
"ton": {
"command": "npx",
"args": ["-y", "@tonnode/mcp"]
}
}
}
Sau đó agent chỉ cần một prompt bình thường:
Lấy địa chỉ
EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2Nvà cho tôi xem nó ở định dạng non-bounceable (UQ) và raw. Kiểm tra xem checksum có hợp lệ không.
Agent sẽ gọi parse_address và trả về bản UQ của đúng cái ví đó, raw hash và trạng thái hợp lệ. Không phải tự xoay xở với base64url bằng tay, cũng không còn rủi ro gõ sai trong 48 ký tự.
get_account_state: biết ví đã deploy hay chưa, trước khi gửi
Chuyện không dừng ở định dạng — bạn còn phải biết địa chỉ đang ở trạng thái nào: active hay uninit. Đây đã là câu hỏi on-chain, và người trả lời là get_account_state. Tool này trả về trạng thái tài khoản, các cờ và dữ liệu về giao dịch gần nhất.
Logic trước khi gửi khoản chuyển đầu tiên:
get_account_statetrả về uninit → địa chỉ chưa được deploy → gửi vào UQ.- trả về active → ví đã deploy → cứ thoải mái gửi vào EQ.
Prompt cho agent:
Kiểm tra qua get_account_state xem địa chỉ
UQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqEBIđã deploy chưa. Nếu trạng thái là uninit — nhắc tôi rằng khoản nạp đầu tiên phải gửi ở định dạng UQ.
Ghép lại thì luồng làm việc như sau: generate_wallet tạo ví (các phiên bản v3r2/v4/v5r1/highload_v3) và trả về địa chỉ mà tại thời điểm tạo chắc chắn là uninit — mạng lưới còn chưa biết gì về nó. Nghĩa là khoản nạp đầu tiên vào đó bắt buộc phải đi vào UQ. Trước khi gửi, get_account_state xác nhận địa chỉ đúng là uninit, còn parse_address bảo đảm bạn đã lấy đúng dạng non-bounceable. Sau khi nạp xong thì tiện tay gọi get_balance để chắc chắn số coin đã nằm lại trên địa chỉ chứ không bật ngược đi.
Một điểm quan trọng về việc tạo ví: generate_wallet trả mnemonic, khóa và địa chỉ về cho người dùng — server không lưu chúng và không ký bất cứ thứ gì thay bạn. Đây là cơ chế phi lưu ký, và nó áp dụng cho toàn bộ tool swap, cross-chain lẫn ví.
Một lưu ý nhỏ về thuật ngữ: GRAM là Toncoin đã đổi tên (đổi vào tháng 6 năm 2026), còn bản thân mạng lưới thì vẫn gọi là TON.
Checklist: làm sao để không mất khoản nạp đầu tiên trên TON
Quy trình ngắn cho mỗi lần chuyển đầu tiên tới một ví mới:
- Đã tạo địa chỉ (
generate_wallethoặc SDK của bạn) — cứ mặc định coi nó là uninit. - Kiểm tra trạng thái qua
get_account_state:uninithayactive. - Nếu uninit — đưa địa chỉ người nhận về dạng UQ bằng
parse_addressvà chỉ gửi khoản nạp đầu tiên vào đó. - Nếu active — dùng
EQđược, mà với contract thìEQcòn là lựa chọn nên ưu tiên. - Với smart contract luôn gửi bounceable (
EQ), để khi trục trặc thì tiền quay về. - Sau khi nạp hãy kiểm tra
get_balance— coin phải nằm lại trên địa chỉ, chứ không bật ngược. - Nhớ rằng:
EQvàUQlà cùng một ví (cùng một raw hash); thứ bạn chọn không phải là địa chỉ, mà là hành vi khi giao không thành công.
Cả hai bước kiểm tra then chốt — parse_address (offline) và get_account_state (on-chain) — đều dùng được ngay, không cần hạ tầng riêng. Hãy lấy key Hobby miễn phí (60 request/phút, không cần thẻ) và gọi cả hai tool thẳng từ AI agent: https://tonnode.io/dashboard?plan=hobby.
Rồi cũng theo đúng công thức đó, bạn có thể xử lý gọn các bài toán lân cận: bắt các khoản thanh toán USDT đến trên TON, đọc số dư USDT trong một lời gọi hay gọi get-method của contract mà không cần SDK. Định dạng địa chỉ là nền móng cho mọi thứ còn lại: hiểu EQ/UQ một lần cho xong — và khoản nạp "bật ngược" sẽ không còn làm bạn bất ngờ nữa.
Cho agent của bạn quyền truy cập TON
16 công cụ MCP: đọc, swap phi lưu ký, cross-chain và ví. Gói miễn phí — 60 req/phút, không cần thẻ.