TON Proof: подтверждение владения кошельком

Запросишь ton_proof при подключении и проверишь на бэкенде, что пользователь владеет адресом кошелька.

Зачем нужен TON Proof

Подключение через TON Connect даёт dApp адрес, но само по себе не доказывает бэкенду, что запрос пришёл от владельца ключа. TON Proof (ton_proof) — подпись специально сформированного сообщения: кошелёк подписывает payload при connect, сервер проверяет подпись и выдаёт свою сессию.

Это аналог «войти через кошелёк», не аналог транзакции: on-chain состояние не меняется.


Запрос proof при подключении

tsx
// @tonconnect/ui-react ^3.0.0
import { useEffect } from "react";
import { useTonConnectUI } from "@tonconnect/ui-react";

export function useTonProofLogin() {
  const [tonConnectUI] = useTonConnectUI();

  useEffect(() => {
    let cancelled = false;

    async function prepareProof() {
      tonConnectUI.setConnectRequestParameters({ state: "loading" });

      const res = await fetch("/api/ton-proof/payload", { method: "POST" });
      const { payload } = (await res.json()) as { payload: string };

      if (cancelled) return;

      tonConnectUI.setConnectRequestParameters({
        state: "ready",
        value: { tonProof: payload },
      });
    }

    prepareProof().catch(() => {
      tonConnectUI.setConnectRequestParameters(null);
    });

    const unsubscribe = tonConnectUI.onStatusChange((wallet) => {
      if (
        wallet?.connectItems?.tonProof &&
        "proof" in wallet.connectItems.tonProof
      ) {
        void fetch("/api/ton-proof/check", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            address: wallet.account.address,
            publicKey: wallet.account.publicKey,
            proof: wallet.connectItems.tonProof.proof,
          }),
        });
      }
    });

    return () => {
      cancelled = true;
      unsubscribe();
    };
  }, [tonConnectUI]);
}

Бэкенд должен: (1) выдать одноразовый payload с коротким TTL, (2) проверить domain/manifest, timestamp, подпись и соответствие адреса публичному ключу. Готовые проверки есть в примерах TON Connect (demo-dapp-with-react-ui) — не изобретай схему байтов вручную.

Пользователь может отклонить connect с proof — тогда tonProof не придёт. Не логинь пользователя «по одному только адресу из фронта».

Как это работает

setConnectRequestParameters({ state: "loading" }) — UI ждёт payload с сервера.

value: { tonProof: payload } — payload попадёт в connect-запрос (QR / universal link).

onStatusChange — после успешного connect смотри wallet.connectItems.tonProof.

Проверка только на сервере. Фронт может показать «проверяем…», но сессию приложения выдаёт бэкенд после валидного proof.

Частые ошибки

Доверяешь account.address без proof → любой может вписать чужой адрес в DevTools. Без серверной проверки подписи это не аутентификация.

Payload без TTL / переиспользование → replay-атака. Генерируй новый payload на каждый connect, храни короткоживущий nonce на бэкенде.

Domain в proof не совпадает с манифестом → кошельки и верификаторы отшивают proof. Domain должен соответствовать URL приложения из манифеста.


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация