SignData in TON via TonConnect: A Developer Guide
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.
The signData
method is implemented in the TonConnect fork by Tonkeeper (opens in a new tab) and is ready for use. You can experiment with data signing in the demo dApp (opens in a new tab). The feature will soon be merged into the main TonConnect repository.
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
Glossary (quick refresher)▼
TonConnect – The protocol connecting your dApp to the user's wallet. It handles connections, transaction requests, and data signing.
SignData – A TonConnect method that allows a dApp to request the user to sign arbitrary data (text, bytes, or Cell) with their wallet. No transaction involved – just a signature.
Cell – The atomic unit of data in TON. Holds up to 1023 bits and 4 references to other cells. Everything in TON – accounts, messages, blocks – is made of Cells. Each Cell has a hash that acts as its unique ID.
TL-B schema – Declarative definition of Cell structure. It’s required for wallets to display signing details and for smart contracts to validate the content.
Ed25519 – The signing algorithm used in TON. It powers both transactions and signData
. Everything signed by the user uses Ed25519 over a SHA256 hash of a strictly formatted message.
Address – The account address in TON. Formed by a workchain
(usually 0
) and a hash of the contract’s code and initialization data. For standard wallets, this hash is deterministically derived from the public key.
Public Key – The public part of the user’s key pair, derived from the private key. Used to verify signatures. In TON, the public key becomes available on-chain only after the wallet is initialized (i.e., has sent at least one transaction). Until then, it’s known only to the dApp via TonConnect and the user.
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...'
}
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: 'EQD...',
timestamp: 1710000000,
domain: 'your-app.com',
payload: {
type: 'text',
text: 'I confirm this action.'
}
}
How the Signature Is Built
For text
and binary
- The message is constructed:
0xffff ++ "ton-connect/sign-data/" ++ Address ++ AppDomain ++ Timestamp ++ Payload
- Components:
Address
: wallet address (workchain + hash)AppDomain
: dApp domain as UTF-8 string with lengthTimestamp
: time of signingPayload
:- Prefix
txt
/bin
- Length
- Text (utf-8 encoded) or bytes
- Prefix
- Then:
Ed25519.sign(sha256(message), privateKey)
For cell
- A Cell is built:
beginCell()
.storeUint(0x75569022, 32)
.storeUint(crc32(schema), 32)
.storeUint(timestamp, 64)
.storeAddress(userWalletAddress)
.storeStringRefTail(appDomain)
.storeRef(cell)
- 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 (opens in a new tab)
How to Verify the Signature
In JavaScript (off-chain)
For text
and binary
, you can reconstruct the message:
import nacl from 'tweetnacl';
- Rebuild the byte array exactly as described above.
- Retrieve the public key (from TonConnect or known in advance).
- Verify:
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 (opens in a new tab)
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 (opens in a new tab)
🔥 On-chain example in FunC: https://github.com/p0lunin/sign-data-contract-verify-example (opens in a new tab)
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.
Format | What’s Signed | Wallet UI | Use Case |
---|---|---|---|
text | Human-readable message | Displayed as-is | Action confirmation, agreements |
binary | Bytes / hashes / identifiers | Shows warning | Document hashes, TX IDs |
cell | Cell with TL-B schema | Schema + warning or content | Smart 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.
Useful Links
- TonConnect fork with
signData
support
github.com/tonkeeper/tonconnect-sdk (opens in a new tab) (includes links to corresponding npm packages) - Demo dApp to test
signData
tonkeeper.github.io/demo-dapp-with-wallet (opens in a new tab) - JS signature verification example
github.com/mois-ilya/ton-sign-data-reference (opens in a new tab) - Smart contract verification example
github.com/p0lunin/sign-data-contract-verify-example (opens in a new tab) - Tonkeeper’s SignData spec
github.com/tonkeeper/ton-connect-docs – SignData (opens in a new tab) - FunC
check_signature
standard lib
docs.ton.org – check_signature (opens in a new tab)