Wallet / Auth·TON
Tonkeeper
Tonkeeper is the leading self-custody wallet for The Open Network (TON), with mobile, desktop, browser-extension, and embedded Telegram-bot variants. Connects to dApps exclusively via TON Connect 2.0 (no `window.tonkeeper` injection on most platforms) — a bridge-based protocol that handles wallet-to-dApp messaging, transaction signing, and proof-based auth without ever exposing keys.
- 01TON dApps and Telegram Mini Apps
- 02TON wallet auth (Tonkeeper, Tonkeeper Pro, Tonkeeper Web)
- 03TON Connect 2.0 transaction signing
- 04TON proof / Sign-In with TON flows
- 05in-Telegram payment UX
- pnpm add @tonconnect/ui-react @tonconnect/sdk
- pnpm add @ton/ton @ton/core
| Variable | Scope | Description |
|---|---|---|
| NEXT_PUBLIC_TONCONNECT_MANIFEST_URL | Client | Public HTTPS URL to your `tonconnect-manifest.json` (name, url, iconUrl). Required by `TonConnectUIProvider` for Tonkeeper to render the app's identity in the connect modal. |
| TON_API_ENDPOINT | Client | TON HTTP API endpoint, e.g. https://toncenter.com/api/v2/jsonRPC (mainnet) or https://testnet.toncenter.com/api/v2/jsonRPC, for chain reads. |
Use Tonkeeper for TON wallet auth via TON Connect. Wrap the app in `<TonConnectUIProvider manifestUrl={NEXT_PUBLIC_TONCONNECT_MANIFEST_URL}>` from `@tonconnect/ui-react`. Read state with `useTonAddress()`, `useTonWallet()`, and `useTonConnectUI()`. To target Tonkeeper specifically, filter the wallets list passed to `<TonConnectButton walletsListConfiguration={{ includeWallets: [{ appName: 'tonkeeper' }] }} />` — `appName` is the canonical identifier published in the TON Connect wallets registry. For transactions, build `{ validUntil, messages: [{ address, amount, payload }] }` (amount in nanoTON, payload as base64 BoC) and call `tonConnectUI.sendTransaction(tx)`. For Sign-In with TON, request a `tonProof` payload at connect time: `tonConnectUI.setConnectRequestParameters({ state: 'ready', value: { tonProof: nonceFromServer } })` then verify the returned signed proof on the server using Tonkeeper's documented proof scheme. Note: there is no `window.tonkeeper` provider in browsers — TON Connect uses an HTTP bridge, not injection.
- ⚑Manifest URL constraints: `tonconnect-manifest.json` must be served over HTTPS with permissive CORS; Tonkeeper rejects http://, localhost (without tunneling like ngrok), and self-signed certs — the connect modal silently fails with no error in production builds.
- ⚑Telegram Mini App context: when running inside Telegram, Tonkeeper-Telegram-Wallet is auto-prioritized over standalone Tonkeeper. Use `@telegram-apps/sdk` to detect `Telegram.WebApp` and explicitly include `'telegram-wallet'` in `includeWallets` if you want both options surfaced.
- ⚑TON address format: Tonkeeper returns user-friendly base64url addresses (`UQ...` / `EQ...`) but contracts often expect the raw `0:hex` form — use `Address.parse(addr).toRawString()` from `@ton/core` before passing to RPC calls; mixing formats causes silent message routing failures.
- ⚑Account model quirk: TON has no EOAs — every wallet is a smart contract that may not yet be deployed. Sending TON to a fresh Tonkeeper address requires the wallet to deploy itself on first outgoing tx; check `client.isContractDeployed(addr)` and surface a 'first-tx is deployment' UX.
- ⚑Async cross-shard messages: a transfer's masterchain block (~5s) does not guarantee shard-level finality — poll by message hash, not sender tx, and expect 1-3 additional blocks for cross-shard settlement.
- ⚑BoC serialization: Tonkeeper expects payloads as base64-encoded Bag of Cells, not JSON. Use `beginCell().storeUint(...).endCell().toBoc().toString('base64')` from `@ton/core` — passing JSON will be silently rejected by the wallet with no user-visible error.
- ⚑Bridge timeouts: TON Connect's HTTP bridge times out user requests after ~5 minutes; show a UX that re-prompts if the user takes too long, and expect `UserRejectsError` for both explicit rejections and bridge expiries.