TON Connect Integration
TON Connect is not just "a wallet for TON", it's a communication protocol between dApp and wallet through a bridge server. Unlike EVM where window.ethereum is injected into the page, TON wallets (Tonkeeper, MyTonWallet, Telegram Wallet) work as separate applications and communicate with dApp through an encrypted channel. This is architecturally important to understand—connection is established asynchronously and can be via QR code or deep link in Telegram.
Installation and Initialization
npm install @tonconnect/ui-react
Manifest is a JSON file shown to user when connecting. Must be accessible over HTTPS at the specified URL:
// public/tonconnect-manifest.json
{
"url": "https://yourapp.com",
"name": "Your dApp",
"iconUrl": "https://yourapp.com/icon.png"
}
// main.tsx
import { TonConnectUIProvider } from '@tonconnect/ui-react'
root.render(
<TonConnectUIProvider manifestUrl="https://yourapp.com/tonconnect-manifest.json">
<App />
</TonConnectUIProvider>
)
Connection Button and State
import { TonConnectButton, useTonConnectUI, useTonAddress, useTonWallet } from '@tonconnect/ui-react'
// Ready-made button with UI
<TonConnectButton />
// Custom logic
function WalletInfo() {
const address = useTonAddress() // user-friendly format
const rawAddress = useTonAddress(false) // raw format for transactions
const wallet = useTonWallet()
const [tonConnectUI] = useTonConnectUI()
return (
<div>
<p>Address: {address}</p>
<p>Wallet: {wallet?.device.appName}</p>
<button onClick={() => tonConnectUI.disconnect()}>Disconnect</button>
</div>
)
}
TON addresses exist in two formats: user-friendly (EQD... or UQD...) and raw (0:abc...). User-friendly includes bounceable/non-bounceable flag. For sending to smart contracts—bounceable (EQ), for regular wallets—non-bounceable (UQ).
Sending Transactions
TON Connect sends messages (messages), not transactions in EVM sense. One transaction can contain multiple messages:
import { useTonConnectUI } from '@tonconnect/ui-react'
import { toNano } from '@ton/ton'
function SendTon() {
const [tonConnectUI] = useTonConnectUI()
async function sendTransaction() {
const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360, // 6 minutes
messages: [
{
address: 'EQD...contractAddress',
amount: toNano('0.05').toString(), // in nanotons
payload: 'te6ccgEBAQEAAgAAAA==', // base64 BOC, optional
}
]
}
const result = await tonConnectUI.sendTransaction(transaction)
// result.boc — BOC of signed transaction
}
}
payload is a BOC (Bag of Cells), serialized structure for contract call. Use @ton/ton to build payload:
import { beginCell } from '@ton/ton'
// Example: call jetton transfer
const body = beginCell()
.storeUint(0xf8a7ea5, 32) // op code transfer
.storeUint(0, 64) // query_id
.storeCoins(toNano('10')) // amount jettons
.storeAddress(destinationAddress)
.storeAddress(responseAddress)
.storeBit(false) // no custom payload
.storeCoins(toNano('0.01')) // forward TON amount
.storeBit(false) // no forward payload
.endCell()
Connection Verification and Session Recovery
TON Connect automatically restores session on page reload (saves to localStorage). To check connection status:
import { useTonConnectUI } from '@tonconnect/ui-react'
const [tonConnectUI] = useTonConnectUI()
// Subscribe to status changes
useEffect(() => {
const unsubscribe = tonConnectUI.onStatusChange((wallet) => {
if (wallet) {
console.log('Connected:', wallet.account.address)
} else {
console.log('Disconnected')
}
})
return unsubscribe
}, [tonConnectUI])
Telegram Mini App Context
In Telegram Mini App, TON Connect works through Telegram Wallet directly—without QR code. TonConnectUIProvider automatically detects context and shows the right UI. For Telegram Web App you need to add @twa-dev/sdk and initialize window.Telegram.WebApp.ready() before provider render.







