TON Connect Protocol

repository·main·Indexed 41 days ago

https://github.com/ton-blockchain/ton-connect

A protocol enabling decentralized applications (dApps) to connect with TON wallets, handling wallet discovery, user authentication, and encrypted communication. Includes specifications for implementing compatible wallets, backend authorization via ton_proof, and RPC methods such as sendTransaction, signMessage, and signData.

Tokens
18.3K
Snippets
30
Records
71
Agent score
52%

What's inside TON Connect

  1. Understand TON Connect deep link forms

    main

    TON Connect uses three types of deep links to initiate connection flows from QR codes, browsers, or native apps:

    1. Universal Link: A wallet-specific HTTPS link defined by the universal_url in the wallets-list. Format: https://<wallet-universal-url>?v=2&id=<hex_client_id>&r=<urlsafe_json_connect_request>&ret=<strategy>&trace_id=<uuid>&e=<base64url_embedded_request>

    2. Unified Deep Link (tc://): A universal scheme supported by wallets that allows a single QR code to work across any compatible wallet. Format: tc://?v=2&id=<hex_client_id>&r=<urlsafe_json_connect_request>&ret=<strategy>&trace_id=<uuid>&e=<base64url_embedded_request>

    3. Custom-scheme Deep Link: A wallet-specific scheme (e.g., tonkeeper-tc://) defined by the deepLink field in wallets-list. These must support the same query parameters as universal links.

    https://<wallet-universal-url>?v=2&id=<to_hex_str(A)>&r=<urlsafe(json.stringify(ConnectRequest))>&ret=back&trace_id=<uuid>&e=<base64url(json.stringify(EmbeddedWireRequest))>
  2. How the `signMessage` RPC method works

    main

    The signMessage method is an RPC operation used when a wallet needs to sign an internal message but not broadcast it to the network.

    Unlike sendTransaction, which broadcasts the transaction to the blockchain, signMessage returns the signed Bag of Cells (BoC) to the dApp. The dApp is then responsible for submitting this signed BoC through a relayer (e.g., for gasless transactions).

    Key differences from sendTransaction:

    1. No Broadcast: The wallet skips the network submission step.
    2. Return Value: The wallet returns the signed BoC instead of a broadcast confirmation.
  3. Backward compatibility for Embedded Requests

    main

    The embedded request mechanism is designed to be non-breaking:

    ScenarioBehaviour
    Old wallet receives URL with eIgnores the unknown parameter and connects normally.
    New wallet receives URL without ePerforms the standard connection flow.
    e carries an unsupported methodThe wallet ignores e and connects normally.
  4. Understand the two bridge variants

    main

    The TON Connect bridge is a transport layer for messages between a dApp and a wallet. There are two distinct types depending on your integration environment:

    1. HTTP bridge: Used for external dApps and services. It requires end-to-end encryption (see session.md) and uses SSE (Server-Sent Events) for receiving messages. The bridge is operated by the wallet provider.
    2. JS bridge: Used when a dApp is opened inside a wallet's webview or when the wallet is a browser extension. It uses an injected binding on window.<walletJsBridgeKey>.tonconnect. Communication is not encrypted as it runs on the same device, and the dApp works with plaintext requests/responses.

    Note: dApp developers do not choose or run the bridge; they must use the bridge URL provided by the specific wallet in the wallets list.

  5. How the HTTP bridge workflow works

    main

    The HTTP bridge uses a relay mechanism to communicate between the dApp and the wallet. The flow follows these steps:

    First-time connection

    1. The dApp subscribes to its bridge SSE (Server-Sent Events) channel.
    2. The dApp sends connection info to the wallet via a universal link, deep link, or QR code.
    3. The wallet reads the parameters, connects to the bridge, and stores connection info locally.
    4. The wallet sends account information to the dApp over the bridge.
    5. The dApp receives the message and stores connection info locally.

    Reconnection

    1. The dApp reads connection info from local storage.
    2. The dApp reconnects to the bridge.
    3. When the user opens the wallet, the wallet reconnects to the bridge using the stored connection info.

    Ordinary requests (Post-connection)

    1. The dApp generates a request and sends it to the bridge.
    2. The bridge forwards the message to the wallet.
    3. The wallet generates a response and sends it to the bridge.
    4. The bridge forwards the response to the dApp.
  6. Detect if a dApp is embedded in a wallet or if the wallet is a browser extension

    main

    Depending on whether you are using the official JS SDK or building a custom implementation, you can detect the environment using the following methods:

    Using the JS SDK

    Use the connector.getWallets() method. The returned wallet entries contain properties to identify the connection type:

    • Embedded dApp: Check the embedded property of the matching entry.
    • Browser Extension: Check the injected property of the matching entry.

    Custom SDK Implementation

    If you are building your own SDK, you can check the global window object:

    • Embedded dApp: Check window.<walletJsBridgeKey>.tonconnect.isWalletBrowser (refer to spec/bridge.md § JS bridge).
    • Browser Extension: Check if window.<walletJsBridgeKey>.tonconnect exists.
  7. Understand the TON Connect RPC Envelope

    main

    TON Connect communication relies on a specific envelope structure for messages exchanged between the dApp and the wallet.

    App Messages consist of ConnectRequest or AppRequest. An AppRequest must include:

    • method: The name of the operation (e.g., sendTransaction, signMessage, signData, disconnect).
    • params: An array of strings containing operation-specific parameters.
    • id: A monotonically increasing string per session. The wallet must reject any request where the id is not strictly greater than the last processed id in that session.

    Wallet Messages consist of WalletResponse or WalletEvent.

    Wallet Responses must have a response.id that exactly matches the request.id it is answering. A response is either a WalletResponseSuccess (containing a result and id) or a WalletResponseError (containing an error object with code, message, and optional data, plus the id).

    Wallet Events are initiated by the wallet (e.g., connect, connect_error, disconnect). They include an event name, a monotonically increasing id (separate from request IDs), and a payload. The dApp must reject any event where the id is not strictly greater than the last processed event id in the session.

    type AppMessage = ConnectRequest | AppRequest;
    type WalletMessage = WalletResponse | WalletEvent;
    
    interface AppRequest {
        method: string;     // 'sendTransaction', 'signMessage', 'signData', 'disconnect'
        params: string[];   // operation-specific parameters
        id: string;         // monotonically increasing per session
    }
    
    interface WalletEvent {
        event: WalletEventName;
        id: number;          // monotonically increasing per session, separate from request IDs
        payload: unknown;
    }
  8. Manage network compatibility and security

    main

    Wallets must strictly enforce network matching to prevent users from sending funds to the wrong network (e.g., sending mainnet funds when a dApp expects testnet).

    Network Rules:

    • Opaque Identifiers: Treat the network field (a stringified TON network global_id) as an opaque identifier. Do not default unknown values to "mainnet".
    • Exact Equality: Enforce exact string equality between the requested network and the wallet's active network.
    • Refuse Cross-Network Requests: If the dApp's network differs from the wallet's current network:
      • Show an alert.
      • MUST NOT allow the transaction to proceed.
      • MUST NOT offer to switch networks.

    Testnet UX:

    • Testnet should be hidden from regular users. Switching to testnet should not be a one-tap action in the main UI, and the wallet should not automatically prompt users to switch to testnet even if the dApp is on it.
  9. Understand the TON Connect connection flow

    main

    The connection flow is the initial exchange in a TON Connect session between a dApp and a wallet.

    1. Request: The dApp sends a ConnectRequest containing a manifestUrl and a list of items (data it wants to share).
    2. Response: The wallet replies with a ConnectEvent.
      • If the user approves, it returns a ConnectEventSuccess containing the requested data items and DeviceInfo.
      • If the user rejects or an error occurs, it returns a ConnectEventError.

    After a successful connection, both parties know each other's client_id, and the dApp has access to the requested data items.

  10. Understand the TON Connect Session protocol

    main

    The Session protocol provides end-to-end encryption between a dApp and a wallet using an untrusted HTTP bridge. The bridge only sees ciphertext and routing addresses.

    Key components include:

    • Client Keypair: Both the dApp and the wallet generate an X25519 keypair per session. The secret key is 32 bytes, and the public key is 32 bytes.
    • client_id: This is the 32-byte public key. On the HTTP bridge, it is represented as a 64-character lowercase hex encoding of the public key.
    • Session: Defined as the pair of client_id values (one from the dApp and one from the wallet). A session is unique to a specific keypair; generating a new keypair creates a new session.
    • Lifecycle: A session lasts until one side discards its keypair. Termination is signaled via the disconnect RPC method (dApp-initiated) or the disconnect event (wallet-initiated).
  11. Use embedded requests (`e`) to combine connection and action

    main

    The e parameter allows you to embed a sendTransaction, signMessage, or signData request directly into the connection link. This enables a single flow where the user connects and performs an action simultaneously.

    • Requirement: Wallets must advertise { name: 'EmbeddedRequest' } in DeviceInfo.features to support this.
    • Format: The value must be base64url(JSON.stringify(EmbeddedWireRequest)) without padding.
    • Compatibility: Wallets MUST accept connect links both with and without the e parameter.
  12. Handle multi-account and account selection

    main

    TON Connect does not use a single "active" account model. Instead, the user selects an account at the moment of confirmation.

    Key Behaviors:

    • No AccountChanged Events: The protocol does not emit events when a user switches accounts within the wallet UI. The dApp's connection is bound to the account selected at the time of connection.
    • Account Switching: To change the account used by a dApp, the user must disconnect and reconnect within the dApp's UI.
    • Enforcing the from field: If a SendTransaction request includes a from field:
      • The wallet MUST NOT allow the user to pick a different sender.
      • If the requested address is unavailable/impossible, show an alert and block the transaction.
    • Session Management: Wallets should provide a UI to list active dApp sessions and allow manual disconnection.