← Back to Academy

SignData in TON via TonConnect - A Developer Guide

Learn how to use SignData method in TonConnect to request cryptographic signatures from user wallets for secure off-chain and on-chain verification.

When it comes to critical actions, a simple button press just doesn't cut it. Real consent feels like your wallet standing tall and declaring: "Yes, it's me! And I approve this!"

A signature is the real deal: cryptographic, verifiable, secure. You can sign anything — text, bytes, or cells. The signature can be verified off-chain in your dApp or passed into a smart contract for on-chain logic.

What is SignData and Why It Matters

Any action where the user must consciously and explicitly confirm their intent – like updating public profile information, linking a new email or phone number, or accepting DAO terms – deserves something more robust than just a click.

SignData lets your dApp request a cryptographic signature – from the user's wallet – on arbitrary content. The user sees exactly what they are signing and can either approve or decline. The signature is generated using the same private key that signs TON transactions, but it provides no direct access to funds.

For a dApp, this signature is a way to secure critical actions, verify a user's real intent, and – if needed – pass validated data into a smart contract for on-chain use.

If you don't clearly get what an Cell, TL-B, or public key is? Read the glossary below

Choosing the Right Format

1. Text

Use this when the data is human-readable.

Pros:

  • The user sees exactly what they are signing.
  • Simple to implement.
  • Perfect for off-chain confirmations.

Example:

{
  type: 'text',
  text: 'I confirm deletion of my account and all associated data.'
}

2. Binary

Use this when signing a hash, arbitrary bytes, or a file.

Pros:

  • Useful for generating digital receipts, hashed content references, etc.
  • Works when the content isn't human-readable or shouldn't be shown.

Example:

{
  type: 'binary',
  bytes: '1Z/SGh+3HFMKlVHSkN91DpcCzT4C5jzHT3sA/24C5A=='
}

3. Cell

Use this if the signed data should be verifiable and restorable inside a smart contract.

Pros:

  • Smart contracts can validate the signature during execution.
  • Supports structured data via TL-B schemas.

Example:

{
  type: 'cell',
  schema: 'message#_ text:string = Message;',
  cell: 'te6ccgEBAQEAVwAAqg+KfqVUbeTvKqB4h0AcnDgIAZucsOi6TLrf...'
}

Clarification on Root Type: If the TL-B schema provided in SignData.cell.schema defines multiple types (for example, one primary type along with auxiliary helper types), the last type definition in the schema is treated as the root type for serialization and deserialization. In practice, this means the cell's content is parsed as an instance of the final type declared in the schema.

How to Request a Signature via TonConnect

const result = await tonConnectUi.signData({
  type: 'text',
  text: 'I confirm this action.',
});

TonConnect handles the request and shows the prompt in the user's wallet. They either sign or decline.

You receive:

{
  signature: 'base64-ed25519-signature',
  address: '0:9a...',
  timestamp: 1710000000,
  domain: 'your-app.com',
  payload: {
    type: 'text',
    text: 'I confirm this action.'
  }
}

How the Signature Is Built

For text and binary

  1. The message is constructed:
0xffff ++ "ton-connect/sign-data/" ++ Address ++ AppDomain ++ Timestamp ++ Payload
  1. Components:
    • Address: wallet address (workchain + hash)
    • AppDomain: dApp domain as UTF-8 string with length
    • Timestamp: time of signing
    • Payload:
      • Prefix txt / bin
      • Length
      • Text (utf-8 encoded) or bytes
  2. Then:
Ed25519.sign(sha256(message), privateKey)

For cell

  1. A Cell is built:
beginCell()
  .storeUint(0x75569022, 32)
  .storeUint(crc32(schema), 32)
  .storeUint(timestamp, 64)
  .storeAddress(userWalletAddress)
  .storeStringRefTail(appDomain)
  .storeRef(cell)
  1. The hash of this Cell is signed:
Ed25519.sign(payload.hash(), privateKey)

🔥 Example signing in JS: https://github.com/mois-ilya/ton-sign-data-reference

How to Verify the Signature

In JavaScript (off-chain)

For text and binary, you can reconstruct the message:

  1. Rebuild the byte array exactly as described above.
  2. Retrieve the public key (from TonConnect or known in advance).
  3. Verify:
import nacl from 'tweetnacl';

const isValid = await nacl.sign.detached.verify(hash, signature, publicKey);

Important: Always ensure that result.address matches the wallet you requested the signature from. Otherwise, an attacker could swap in a valid signature from a different wallet.

🔥 Example verification in JS: https://github.com/mois-ilya/ton-sign-data-reference

In a Smart Contract (on-chain)

For the cell format, smart contracts can verify the signature directly.

They must validate:

  • Prefix: 0x75569022
  • Schema hash matches
  • Timestamp is recent
  • Address matches
  • App domain matches
  • Signature is valid using ed25519

📄 check_signature in standard library

🔥 On-chain example in FunC: https://github.com/p0lunin/sign-data-contract-verify-example


TL;DR

The SignData method in TonConnect is a secure way for a dApp to request a user's cryptographic signature on text, bytes, or TON cells – directly through their wallet. It's not a transaction, but a confirmation that can be verified off-chain or on-chain.

  • Users see what they sign (if it's text or a proper TL-B schema).
  • Signature uses the same key as for transactions, but doesn't send anything.
  • Verification works anywhere – on your backend or inside a smart contract.
FormatWhat's SignedWallet UIUse Case
textHuman-readable messageDisplayed as-isAction confirmation, agreements
binaryBytes / hashes / identifiersShows warningDocument hashes, TX IDs
cellCell with TL-B schemaSchema + warning or contentSmart contract validation

If you're building a dApp that needs real user consent and want to verify it without extra hassle – SignData gets the job done.