Skip to main content
The verification utilities work standalone: a frontend (for example, a JS app using TonConnect UI) connects the wallet and sends the resulting proof or signature to your Python backend, which validates it cryptographically. Never trust a client-reported address — only a verified TON Proof or signed-data payload proves that the user actually controls the private key of the declared address.

Verifying TON Proof

TON Proof is connection-time authentication: the wallet signs a message bound to its address, your app’s domain, a timestamp, and a backend-issued payload nonce. The frontend sends your backend a dict in this shape:
Validate the dict into a typed DTO with TonProofPayloadDto.model_validate(...) and verify it with VerifyTonProof. Verification performs five checks and raises nacl.exceptions.BadSignatureError if any fails:
  1. The public key extracted from walletStateInit matches the publicKey field.
  2. The address derived from walletStateInit matches the address field.
  3. The timestamp is within the valid_auth_time window (prevents replay).
  4. The domain is in allowed_domains (prevents cross-app reuse).
  5. The Ed25519 signature over the ton-proof-item-v2 message is valid.
The public key can only be extracted locally from walletStateInit for known standard wallet contracts (V1-V5). For wallet contracts the library does not recognize, pass an async resolver as get_wallet_public_key to verify(...) — it receives the wallet Address and should return the PublicKey (for example, by calling the contract’s get_public_key get method on-chain) or None.

Verifying Signed Data

Unlike TON Proof, signData is called explicitly to sign off-chain content — agreeing to terms, authorizing actions, and similar. The frontend sends a flat dict with the signature fields at the top level:
payload is the data the user signed; supported types are text, binary, and cell. Verification follows the same pattern with SignDataPayloadDto and VerifySignData, performing the same five checks. The canonical signing message varies by payload type: text and binary use SHA-256, cell payloads use a TL-B cell hash with a CRC32 schema fingerprint.

Challenge Payloads

A TON Proof is only meaningful when its payload nonce was issued by your backend. Otherwise an attacker could replay a valid proof the user produced for another service. The library provides an HMAC-based challenge that requires no server-side session state:
The payload contains random bytes, an expiry timestamp, and an HMAC-SHA256 signature under your secret key. verify_ton_proof_payload confirms the nonce was issued by your backend and is still within its TTL, which binds the proof to your service and prevents replay. Run it before VerifyTonProof(...).verify(...). For the full connect-time flow where the same Python process both connects the wallet and verifies the proof, see Connect Wallet.

check_ton_proof.py

Standalone TON Proof verification example on GitHub.

check_sign_data.py

Standalone signed-data verification example on GitHub.