diff --git a/docs.json b/docs.json
index 7af88544..46e8046d 100644
--- a/docs.json
+++ b/docs.json
@@ -368,6 +368,7 @@
"pages": [
"techniques/carry-value",
"techniques/contract-sharding",
+ "techniques/signing",
"techniques/security",
"techniques/gas",
"techniques/using-onchain-libraries",
diff --git a/resources/dictionaries/custom.txt b/resources/dictionaries/custom.txt
index ad4a8da2..5fc8098c 100644
--- a/resources/dictionaries/custom.txt
+++ b/resources/dictionaries/custom.txt
@@ -604,6 +604,7 @@ savelists
sbt
scannability
sdks
+secp
semver
seqno
seqnos
@@ -662,6 +663,7 @@ systemd
tApp
tApps
tagless
+telemint
testnet
tock
tokenomics
diff --git a/resources/dictionaries/tvm-instructions.txt b/resources/dictionaries/tvm-instructions.txt
index 202e8644..bc4d44ce 100644
--- a/resources/dictionaries/tvm-instructions.txt
+++ b/resources/dictionaries/tvm-instructions.txt
@@ -275,6 +275,7 @@ GETSTORAGEFEE
GLOBALID
GREATER
GTINT
+HASHBU
HASHCU
HASHEXT
HASHEXTAR_BLAKE
diff --git a/techniques/signing.mdx b/techniques/signing.mdx
new file mode 100644
index 00000000..2b4b5670
--- /dev/null
+++ b/techniques/signing.mdx
@@ -0,0 +1,361 @@
+---
+title: "Signing messages in TON"
+sidebarTitle: "Signing"
+---
+
+import { Aside } from '/snippets/aside.jsx';
+
+## Overview
+
+Cryptographic signatures are the foundation of access control in TON. Contracts verify signatures on-chain and implement their own policies, enabling flexible designs such as wallet operations, server-authorized actions, gasless flows, multisig, and delegation.
+
+### Ed25519 in TON
+
+TON uses Ed25519 as the standard signature scheme. All wallets (v1–v5) and highload wallets rely on Ed25519.
+
+**Specification:**
+
+- **Public key:** 256 bits
+- **Signature:** 512 bits
+- **Curve:** Edwards form over Curve25519
+
+See [TON Cryptography — Ed25519](/ton/whitepapers/tblkch#a-3-10-cryptographic-ed25519-signatures) for cryptographic details.
+
+
+
+
+
+### What gets signed
+
+Ed25519 signatures in TON work with **hashes**, not raw data:
+
+1. **Off-chain:** Serialize message data into a cell → compute its hash (256 bits) → sign the hash with private key → signature (512 bits)
+1. **On-chain:** Contract receives signature and data → recomputes the hash → verifies signature matches the hash and public key
+
+## Common signing patterns
+
+Signatures are used in different ways depending on who signs the message, who sends it, and who pays for execution. Here are three real-world examples.
+
+### Example 1: Standard wallets (v1–v5)
+
+**How it works:**
+
+1. User signs a message off-chain (includes replay protection data and transfer details)
+1. User sends external message to blockchain
+1. Wallet contract verifies the signature
+1. Wallet contract checks seqno for replay protection
+1. Wallet contract accepts message (pays gas from wallet balance)
+1. Wallet contract increments seqno
+1. Wallet contract executes the transfer
+
+**Key characteristics:**
+
+- **Who signs:** User
+- **Who sends:** User (external message)
+- **Who pays gas:** Wallet contract
+
+This is the most common pattern.
+
+### Example 2: Gasless transactions (Wallet v5)
+
+**How it works:**
+
+1. User signs a message off-chain that includes two transfers: one to recipient, one to service as payment
+1. User sends signed message to service via API
+1. Service verifies the signature
+1. Service wraps signed message in internal message
+1. Service sends internal message to user's wallet (pays gas in TON)
+1. Wallet contract verifies user's signature
+1. Wallet contract checks seqno for replay protection
+1. Wallet contract increments seqno
+1. Wallet contract executes both transfers (to recipient and to service)
+
+**Key characteristics:**
+
+- **Who signs:** User
+- **Who sends:** Service (internal message)
+- **Who pays gas:** Service (in TON), gets compensated in Jettons
+
+This pattern enables users to pay gas in Jettons instead of TON.
+
+### Example 3: Server-controlled operations
+
+**How it works:**
+
+1. User requests authorization from server
+1. Server validates request and signs authorization message (includes validity period and operation parameters)
+1. User sends server-signed message to contract (with payment)
+1. Contract verifies server's signature
+1. Contract checks validity period
+1. Contract performs authorized action (deploy, mint, claim)
+1. If user tries to send same message again, contract ignores it (state already changed)
+
+**Key characteristics:**
+
+- **Who signs:** Server
+- **Who sends:** User (internal message with payment)
+- **Who pays gas:** User
+
+This pattern is useful when backend needs to authorize specific operations (auctions, mints, claims) without managing private keys for each user.
+
+**Real-world example:** [telemint contract](https://github.com/TelegramMessenger/telemint) uses server-signed messages to authorize NFT deployments.
+
+## Message structure for signing
+
+When designing a signed message, you choose how to organize the signed data — the message fields that will be hashed and verified. The key question: is the signed data a **slice** (part of a cell) or a **cell** (separate cell)? This affects gas consumption during signature verification.
+
+### Approach 1: Signed data as slice
+
+After loading the signature from the message body, the signed data remains as a **slice** — a part of the cell that may contain additional data and references.
+
+**Used in:** Wallet v1-v5
+
+**Schema — Wallet v3r2:**
+
+```tlb
+msg_body$_ signature:bits512 subwallet_id:uint32
+ seqno:uint32 valid_until:uint32
+ mode:uint8 message_to_send:^Cell
+ = ExternalInMessage;
+```
+
+```mermaid
+graph
+ subgraph Cell["Message body"]
+ direction LR
+ A[**signature**]
+ subgraph Signed["Signed data"]
+ direction LR
+ B[subwallet_id]
+ C[seqno]
+ D[valid_until]
+ E[mode]
+ F((message to send))
+ end
+ end
+
+ A ~~~ B
+ B ~~~ C
+ C ~~~ D
+ D ~~~ E
+ E ~~~ F
+
+```
+
+**Verification in FunC:**
+
+```func
+slice signature = in_msg_body~load_bits(512);
+slice signed_data = in_msg_body; // Remaining data
+
+int hash = slice_hash(signed_data); // 526 gas
+throw_unless(35, check_signature(hash, signature, public_key));
+```
+
+**Gas analysis:**
+
+After loading the signature, the remaining data is a **slice**. To verify the signature, the contract needs to hash this slice. In TVM, the method for hashing a slice is `slice_hash()`, which costs 526 gas.
+
+**Why expensive?**\
+`slice_hash()` internally rebuilds a cell from the slice, copying all data and references.
+
+
+
+### Approach 2: Signed data as cell
+
+The signed data is stored in a **separate cell**, placed as a reference in the message body.
+
+**Used in:** Preprocessed Wallet v2, Highload Wallet v3
+
+**Schema — Preprocessed Wallet v2:**
+
+```tlb
+_ valid_until:uint64 seqno:uint16 actions:^Cell = MsgInner;
+
+msg_body$_ signature:bits512 msg_inner:^MsgInner = ExternalInMessage;
+```
+
+```mermaid
+graph LR
+ subgraph Root["Message body"]
+ direction LR
+ A[**signature**]
+ B((msg_inner))
+ end
+
+ subgraph Ref["Signed data (next cell)"]
+ direction LR
+ C[valid_until]
+ D[seqno]
+ E((actions))
+ end
+
+ A ~~~ B
+ B -.-> Ref
+ C ~~~ D
+ D ~~~ E
+
+```
+
+**Verification in FunC:**
+
+```func
+slice signature = in_msg_body~load_bits(512);
+cell signed_data = in_msg_body~load_ref(); // Signed data as cell
+
+int hash = cell_hash(signed_data); // 26 gas
+throw_unless(35, check_signature(hash, signature, public_key));
+```
+
+**Gas analysis:**
+
+The signed data is loaded as a **cell** from the reference. To get its hash, the contract uses `cell_hash()`, which costs only 26 gas.
+
+**Why efficient?**\
+Every cell in TON stores its hash as metadata. `cell_hash()` reads this precomputed value directly — no rebuilding, no copying.
+
+**Trade-off:**\
+This approach adds one extra cell to the message, slightly increasing the forward fee. However, the gas savings (\~500 gas) outweigh the forward fee increase.
+
+### TVM v12 improvement
+
+TVM version 12 introduced efficient builder hashing (`HASHBU` instruction), which makes **signed data as slice** approach much more gas-efficient.
+
+**Verification in FunC (TVM v12+):**
+
+```func
+slice signature = in_msg_body~load_bits(512);
+slice signed_data = in_msg_body;
+
+builder b = begin_cell().store_slice(signed_data);
+int hash = b.builder_hash();
+throw_unless(35, check_signature(hash, signature, public_key));
+```
+
+**Gas comparison:**
+
+| Method | Gas cost | Notes |
+| ----------------------- | --------- | -------------------------- |
+| `slice_hash()` | 526 gas | Rebuilds cell from slice |
+| Builder hashing (slice) | \<100 gas | v12+: cheap, uses HASHBU |
+| `cell_hash()` (cell) | 26 gas | Uses precomputed cell hash |
+
+**Conclusion:**\
+In TVM v12+, both approaches are gas-efficient. New contracts can choose based on code simplicity and forward fee considerations.
+
+Reference: [GlobalVersions.md — TVM v12 updates](https://github.com/ton-blockchain/ton/blob/master/doc/GlobalVersions.md#new-tvm-instructions-4)
+
+## How to sign messages in TypeScript
+
+### Prerequisites
+
+- Node.js 18+ or TypeScript environment
+- `@ton/core`, `@ton/crypto` packages installed
+
+Install required packages:
+
+```bash
+npm install @ton/core @ton/crypto
+```
+
+### Step 1: Generate or load a mnemonic
+
+A mnemonic is your wallet's master secret. It derives the private key used to sign messages.
+
+**Generate a new mnemonic:**
+
+```typescript
+import { mnemonicNew } from '@ton/crypto';
+
+const mnemonic = await mnemonicNew(24); // Array of 24 words
+```
+
+**Load an existing mnemonic:**
+
+```typescript
+const mnemonic = 'word1 word2 word3 ... word24'.split(' ');
+```
+
+
+
+### Step 2: Derive the keypair
+
+Convert the mnemonic to an Ed25519 keypair:
+
+```typescript
+import { mnemonicToPrivateKey } from '@ton/crypto';
+
+const keyPair = await mnemonicToPrivateKey(mnemonic);
+// keyPair.publicKey — stored in contract
+// keyPair.secretKey — used to sign messages
+```
+
+### Step 3: Build the signed data
+
+Build the message data that will be signed:
+
+```typescript
+import { beginCell } from '@ton/core';
+
+// Build signed data (example: wallet message)
+const seqno = 5;
+const validUntil = Math.floor(Date.now() / 1000) + 60; // 60 seconds from now
+
+const signedData = beginCell()
+ .storeUint(seqno, 32)
+ .storeUint(validUntil, 32)
+ // ... other fields (subwallet_id, actions, etc.)
+ .endCell();
+```
+
+### Step 4: Create the signature
+
+Sign the hash of the signed data:
+
+```typescript
+import { sign } from '@ton/crypto';
+
+const signature = sign(signedData.hash(), keyPair.secretKey);
+// signature is a Buffer with 512 bits
+```
+
+### Step 5: Build the message body
+
+Choose the structure based on your contract (see [Message structure for signing](#message-structure-for-signing) above):
+
+```typescript
+// Approach 1: Signed data as slice
+const messageBodyInline = beginCell()
+ .storeBuffer(signature) // 512 bits
+ .storeSlice(signedData.asSlice()) // Signed data as slice
+ .endCell();
+
+// Approach 2: Signed data as cell
+const messageBodySeparate = beginCell()
+ .storeBuffer(signature) // 512 bits
+ .storeRef(signedData) // Signed data as cell
+ .endCell();
+```