- Key Takeaway 1: Signing a message with your wallet proves you own a private key — no transaction, no gas fee required.
- Key Takeaway 2: Ethereum uses ECDSA with the EIP-191 standard; Solana uses Ed25519; Chia uses BLS-12-381 — each chain has different code and tooling.
- Key Takeaway 3: Never sign a raw hash you don’t understand. Phishing attacks often disguise wallet-draining approvals as simple “message” requests.
- Key Takeaway 4: Chia’s BLS signatures can be aggregated, meaning dozens of signatures collapse into one — a major advantage for developers building multi-party apps.
- Key Takeaway 5: Sign-In With Ethereum (SIWE) is the most widely deployed real-world use of message signing, letting users log in to dApps without a password.
When you sign and verify messages on a blockchain, you use your wallet’s private key to create a cryptographic proof that only you could have made — then anyone can check that proof using only your public key or wallet address, without you ever revealing the private key. This process happens entirely off-chain, costs no gas, and is one of the most useful tools in a blockchain developer’s toolkit.
What Does It Mean to Sign and Verify Messages on a Blockchain?
Think of it like a wax seal on a letter. In the old days, a king pressed his unique ring into hot wax to prove a letter came from him. Anyone who knew what his seal looked like could verify it was real. Message signing on a blockchain works the same way — your private key is the ring, and the signature is the wax seal. The message can be anything: a sentence, a number, or a complex JSON object.
How a Digital Signature Works (Plain English)
Every blockchain wallet has two keys: a private key (secret, never shared) and a public key (safe to share). When you sign a message, your wallet runs a math operation that combines your private key with the message to produce a unique string of characters called a signature. The signature is different for every message, so you can’t reuse one. When someone wants to verify it, they run a separate math operation using the message, the signature, and your public address. If the result checks out, the signature is valid — and that proves you wrote the original message. Your private key never leaves your wallet during this whole process.[1]
Why Does This Cost No Gas?
Gas fees on chains like Ethereum pay for work done by network validators. When you sign a message, you are not asking the network to do anything — you are doing the math locally on your own device. The signature only touches the blockchain if a smart contract needs to verify it on-chain (for example, in a permit or meta-transaction flow). For most everyday uses, like proving wallet ownership to a website, the whole process stays off-chain and is completely free.[2]
| Situation | Use Message Signing? | Why |
|---|---|---|
| Logging into a dApp | ✅ Yes | Prove wallet ownership without sending funds |
| Sending tokens to another wallet | ❌ No | Requires a real transaction with gas |
| Verifying you control an address (KYC-lite) | ✅ Yes | Off-chain proof, no fee, instant |
| Whitelisting for an NFT mint | ✅ Yes | Operator signs your address; you submit on mint |
| Approving a token spend | ❌ No — use EIP-2612 Permit | Requires on-chain state change |
| Proving OTC deal ownership off-chain | ✅ Yes | Timestamped, verifiable proof of control |
| Interacting with a Chialisp puzzle | ✅ Yes (AGG_SIG_ME condition) | Coin won’t spend unless signature is present |
How to Sign and Verify Messages on Ethereum
Ethereum is where message signing is most standardized. The core tool is the personal_sign method, which follows the EIP-191 specification. EIP-191 prepends a special prefix — \x19Ethereum Signed Message:\n followed by the message length — before hashing and signing. This prefix was invented specifically so that a signed message could never be mistaken for a raw Ethereum transaction, which protects users from a class of attacks where malicious apps trick wallets into signing real transactions disguised as messages.[3]
The EIP-191 Standard Explained
Before EIP-191 existed, Ethereum had no standard way to tell a signed message apart from signed transaction data. This was dangerous. A malicious website could hand you something that looked like a harmless message but was actually a transaction ready to drain your wallet. EIP-191 solved this by defining a fixed format: 0x19 as the first byte (a value that is invalid as the start of any Ethereum transaction), followed by a version byte, and then the message. The most common version — 0x45, the letter “E” — is what MetaMask uses every time you sign a message through personal_sign.[3] The result is that any EIP-191 signed payload can mathematically never be a valid transaction, making it safe to sign through any EIP-191-compliant wallet.
Sign-In With Ethereum (SIWE) — A Real-World Use Case
The most widely used application of Ethereum message signing is Sign-In With Ethereum, defined by ERC-4361. Instead of entering a username and password on a dApp, you sign a structured message that includes the site’s domain, a one-time nonce to prevent replay attacks, and an expiration time. The server checks the signature, extracts your address, and creates a session — no password database required.[4]
“Sign-In with Ethereum describes how Ethereum accounts authenticate with off-chain services by signing a standard message format parameterized by scope, session details, and security mechanisms.”— ERC-4361, Wayne Chang, Jack Tanner et al., Ethereum Improvement Proposals
Case study: Following ERC-4361’s finalization in August 2025, major wallet providers including MetaMask added native SIWE parsing, displaying a structured, human-readable login prompt instead of a raw hex string — directly reducing phishing risk for millions of users.[11]
Code: Sign and Verify with ethers.js (v6)
The code below shows how to sign a message in the browser and then verify the signer’s address on a backend server using ethers.js. Each step is commented so you can follow along even if you’re new to JavaScript.
// ---------- CLIENT SIDE (browser) ----------
import { ethers } from "ethers";
// Connect to the user's wallet (e.g., MetaMask)
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// The message to sign - keep it human-readable so the user knows what they're approving
const message = "I confirm I own this wallet. Nonce: 8f3k2a";
// personal_sign wraps the message in the EIP-191 prefix before signing
const signature = await signer.signMessage(message);
console.log("Signature:", signature);
// e.g. "0x1b0c9cf8..."
// ---------- SERVER SIDE (Node.js) ----------
import { ethers } from "ethers";
const message = "I confirm I own this wallet. Nonce: 8f3k2a";
const signature = "0x1b0c9cf8..."; // received from client
// verifyMessage re-applies the EIP-191 prefix and recovers the signer address
const recoveredAddress = ethers.verifyMessage(message, signature);
// Compare to the address the user claimed
if (recoveredAddress.toLowerCase() === claimedAddress.toLowerCase()) {
console.log("✅ Signature valid — address verified");
} else {
console.log("❌ Signature invalid");
}
The key thing to understand here is that verifyMessage does not need the private key at all. It uses math to reconstruct which key must have created this signature for this exact message. If the answer matches the address on file, you have proof.[2]
How to Sign and Verify Messages on Solana
Solana uses a different cryptographic algorithm called Ed25519, which is part of the Edwards-curve Digital Signature Algorithm family. Ed25519 produces small, fast signatures: public keys are 32 bytes, and signatures are 64 bytes. Solana chose Ed25519 because it verifies faster than ECDSA and has no dangerous nonce-reuse pitfall — if you accidentally reuse a nonce in ECDSA, your private key can be extracted by anyone watching.[8] Ed25519 is deterministic, meaning the same private key and message always produce the same signature, with no nonce involved at all.
Ed25519 — Solana’s Signature Scheme
For off-chain message signing on Solana, the standard library is TweetNaCl (tweetnacl on npm), which is the same cryptography library that Solana’s own @solana/web3.js uses internally. To verify an off-chain signature inside a Solana program (on-chain), Solana provides a native precompile called Ed25519Program — similar to how Ethereum uses an ecrecover precompile — because running full Ed25519 in the Solana VM would consume too many compute units.[8]
Code: Sign and Verify with TweetNaCl (JavaScript)
Here is a clean example of signing a message off-chain and verifying it, using the same library Solana uses under the hood.
import nacl from "tweetnacl";
import bs58 from "bs58";
// ---- SIGNING ----
// Generate a keypair (in production, derive this from a wallet mnemonic)
const keypair = nacl.sign.keyPair();
// Encode the message as bytes — nacl works with Uint8Array, not strings
const message = new TextEncoder().encode("Prove wallet ownership: nonce-7x9q");
// sign.detached produces a 64-byte signature WITHOUT prepending the message
const signature = nacl.sign.detached(message, keypair.secretKey);
console.log("Public key (base58):", bs58.encode(keypair.publicKey));
console.log("Signature (base58): ", bs58.encode(signature));
// ---- VERIFYING ----
// Anyone can verify using only: message + signature + publicKey
const isValid = nacl.sign.detached.verify(message, signature, keypair.publicKey);
console.log("Valid?", isValid); // true
Notice that Solana does not add any special prefix to the message the way Ethereum’s EIP-191 does. The raw message bytes are signed directly. This is simpler but means you, the developer, are responsible for making sure the message your users see is clear and human-readable before they approve it in their wallet.
How Chia Does It Differently: BLS Signatures and AGG_SIG_ME
Chia Network takes a fundamentally different approach to signatures. Instead of ECDSA (Ethereum/Bitcoin) or Ed25519 (Solana), Chia uses BLS-12-381 (Boneh–Lynn–Shacham) signatures. On the surface, signing still works the same way: private key plus message equals signature. But BLS unlocks something no other signature scheme does natively: non-interactive aggregation. Multiple signatures from different keys on different messages can be mathematically combined into a single signature that is just as small and fast to verify as one.[7]
BLS aggregation means a Chia block can carry thousands of coin spends, each with its own signature, yet the entire block needs only one aggregated signature to verify. This keeps block sizes small and validation fast, even under high load.
Signing a Message with the Chia CLI
The quickest way to sign and verify a message on Chia is through the command-line tools built into the full node software. This is perfect for testing key ownership, writing backend scripts, or building tooling for custody workflows.
# --- SIGN a message using your wallet key ---
# Replace FingerPrint with your actual wallet fingerprint (from: chia keys show)
# The hd_path m/12381/8444/2/0 targets your first wallet key
chia keys sign \
--fingerprint "YourFingerPrint" \
--hd_path "m/12381/8444/2/0" \
--message "I own this Chia address"
# Output:
# Public key: b8f7dd239557ff8c49d338f89ac1a258...
# Signature: 91c3d0504c2c5e02091f92cf0c3f79f2...
# --- VERIFY the message ---
chia keys verify \
--public_key "b8f7dd239557ff8c49d338f89ac1a258..." \
--signature "91c3d0504c2c5e02091f92cf0c3f79f2..." \
--message "I own this Chia address"
# Output: True
The HD path m/12381/8444/2/0 tells Chia which derived key to use. The 12381 segment is specific to BLS signatures, and 8444 is Chia’s network identifier. You do not need to change these values for standard wallet signing — only the final index 0 changes if you want to use a different key slot.[5]
The AGG_SIG_ME Condition in Chialisp
When you move from off-chain signing to actually enforcing signatures inside Chia smart contracts (called puzzles), you use the AGG_SIG_ME condition in Chialisp. This condition tells the network: “This coin may only be spent if the spend bundle contains a valid BLS signature from this specific public key, over this specific message.” If the signature is missing or wrong, the coin stays locked.
; --- Chialisp puzzle that requires a valid BLS signature to spend ---
(mod (PUBLIC_KEY conditions)
(include condition_codes.clib)
(include sha256tree.clib)
; AGG_SIG_ME = condition code 50
; It demands: a signature from PUBLIC_KEY over (sha256tree conditions)
; The message automatically includes the coin_id and genesis_challenge
; so the signature cannot be replayed on a different coin or network
(c
(list AGG_SIG_ME PUBLIC_KEY (sha256tree conditions))
conditions
)
)
The reason the message for AGG_SIG_ME includes the coin ID and genesis challenge is replay protection. On Ethereum, EIP-191 and EIP-712 handle this by encoding the chain ID and nonce into the message you sign. Chialisp handles it at the protocol level — the network bakes the coin’s unique identity into the required message, so a signature created for coin A on mainnet cannot be used to spend coin B on testnet. This is the same concept as Ethereum’s EIP-191 prefix, just implemented at a lower layer.[6]
Why BLS Signatures Are Special for Developers
If you are building multi-party tools — custody systems, shared treasuries, or M-of-N approvals — BLS signatures save enormous effort. On Ethereum, collecting three signatures for a 3-of-5 multisig means storing and verifying three separate ECDSA signatures, each consuming extra gas. With Chia’s BLS aggregation, all three signatures are folded into one before the spend bundle even hits the mempool. The network verifies a single aggregated signature, regardless of how many parties signed. This is not just cheaper — it’s architecturally cleaner and harder to exploit.[7]
| Feature | ECDSA / secp256k1 (Ethereum) | Ed25519 (Solana) | BLS-12-381 (Chia) |
|---|---|---|---|
| Signature size | 65 bytes (r, s, v) | 64 bytes | 96 bytes (G2 element) |
| Public key size | 33 bytes (compressed) | 32 bytes | 48 bytes (G1 element) |
| Native aggregation | ❌ No | ❌ No | ✅ Yes (non-interactive) |
| Deterministic signing | ❌ No (nonce required) | ✅ Yes | ✅ Yes |
| Single verify speed | Fast (~0.3 ms) | Faster (~0.1 ms) | Slower (~1.1 ms) but batch-verifies efficiently |
| Message prefix / standard | EIP-191 / EIP-712 | Raw bytes (no standard prefix) | Protocol-level (coin ID + genesis challenge in AGG_SIG_ME) |
| Off-chain signing CLI | MetaMask, Etherscan, ethers.js | Phantom, @solana/web3.js, TweetNaCl | chia keys sign / chia keys verify |
| On-chain verification | ecrecover precompile (Solidity) | Ed25519Program (Solana native) | AGG_SIG_ME / AGG_SIG_UNSAFE (Chialisp conditions) |
| Best for | General EVM dApps, SIWE login | High-throughput SPL programs | Multi-party custody, compact blocks |
Security Warnings: What You Should Never Sign
Signing a simple text message is safe. The danger comes when you sign something you don’t fully understand. Phishing attacks increasingly use the eth_sign method (not personal_sign) to present raw 32-byte hashes for signing. Unlike personal_sign, eth_sign does not add the EIP-191 prefix — meaning the data you sign could be a valid transaction, a token approval, or anything else the attacker constructs. If you sign it, you could hand over control of your funds instantly.[9]
Never sign a raw hex hash unless you have verified exactly what it represents at the byte level. Legitimate dApps that use message signing for authentication (like SIWE) will always show you a plain, human-readable message in your wallet — not a string of hex characters.
On Solana, the equivalent risk is signing a transaction disguised as a message. Hardware wallets like Ledger and Trezor add an extra layer of protection by displaying what you are signing on the device screen before you approve, so always confirm the message on the device, not just in the browser. On Chia, the AGG_SIG_ME condition’s built-in coin ID binding helps prevent replay attacks, but you should still review what conditions you are authorizing before signing a spend bundle.
The Chialisp Connection: Puzzles as Signature Policies
If you are coming to Chia from Ethereum or Solana, here is a useful way to think about how signatures fit into the two models. On Ethereum, your wallet address is an account that holds a balance. A signature proves you own the account, and the smart contract does the rest. On Chia, every coin is governed by a Chialisp puzzle — a piece of code that defines the exact conditions required to spend that coin. The AGG_SIG_ME condition is how you embed a signature requirement directly into the coin’s spending rules. It is similar to require(ecrecover(hash, v, r, s) == owner) in Solidity, but evaluated at the protocol level rather than inside a contract. There is no “contract address” to hack — the rule is the coin itself.
This is exactly the architecture that allows Chia to offer features like time-locked spending, M-of-N multisig, and clawback paths as native coin conditions, without needing smart contract layers on top. If you have been exploring Chia stablecoins like USDS, those coins also depend on signature-enforced spending rules at the Chialisp level — the same cryptography described in this article, just applied to DeFi primitives. To learn how stablecoins work in the Chia ecosystem, check out our guide: Stablecoins on Chia Network: USDS and the Green Blockchain Revolution.
Conclusion
Signing and verifying messages on a blockchain is one of the first things you should add to your developer toolkit, no matter which L1 you work on. It is free, immediate, and gives you a cryptographically solid way to prove wallet ownership, authenticate users, and build trust into your applications. Ethereum’s EIP-191 and SIWE standard make this straightforward for EVM chains. Solana’s Ed25519 and TweetNaCl handle it cleanly in JavaScript or Python. And Chia’s BLS signatures, built directly into Chialisp via AGG_SIG_ME, give you native aggregation and replay protection that other chains are still trying to bolt on. Start with the CLI commands and ethers.js examples in this article, then work your way up to embedding signatures in your smart contracts or Chialisp puzzles. The cryptography does the heavy lifting — you just need to know which tool to reach for.
sign verify messages blockchain FAQs
What does it mean to sign verify messages blockchain?
To sign and verify messages on a blockchain means using your wallet’s private key to create a unique digital proof for a piece of text, then having anyone confirm that proof using your public address. Signing happens locally on your device, requires no gas, and your private key is never exposed. Verification is open to anyone who has the original message, the signature, and your public address.
Can you sign and verify messages on a blockchain without sending a transaction?
Yes — this is the whole point of off-chain message signing. The math runs entirely on your device; nothing touches the blockchain unless a smart contract later needs to check the signature on-chain. This makes it free and instant for most use cases like login authentication or ownership proofs.
What is the difference between eth_sign and personal_sign on Ethereum?
personal_sign follows the EIP-191 standard, which prepends a human-readable prefix so a signed message can never accidentally be a valid transaction. eth_sign signs raw data with no prefix, making it potentially dangerous if a malicious app feeds you a disguised transaction hash to sign. Always prefer personal_sign (or EIP-712 for structured data) over eth_sign.
How do you sign verify messages blockchain on Chia vs. Ethereum?
On Ethereum, you use personal_sign in your wallet and verifyMessage in ethers.js, both following the EIP-191 standard. On Chia, you use chia keys sign and chia keys verify in the CLI for off-chain proofs, or embed the AGG_SIG_ME condition in a Chialisp puzzle to enforce signatures on-chain at the protocol level. The core concept — private key signs, public key verifies — is identical; only the algorithm (ECDSA vs. BLS) and tooling differ.
Is BLS signature aggregation on Chia better than ECDSA multisig on Ethereum?
For multi-party use cases, BLS aggregation on Chia is significantly more efficient. Multiple BLS signatures collapse into one before submission, so verification cost stays flat regardless of how many parties signed. Ethereum ECDSA multisig requires storing and verifying each signature separately, which increases gas cost linearly with the number of signers. The tradeoff is that individual BLS verification is slightly slower than a single ECDSA check, but at the block level, BLS wins on throughput.
sign verify messages blockchain Citations
- EIP-191: Signed Data Standard — Ethereum Improvement Proposals (Martin Holst Swende, Nick Johnson, January 2016)
- Signing — ethers.js v6 Cookbook, Richard Moore (ricmoo)
- Understanding Ethereum Signature Standards EIP-191 & EIP-712 — Cyfrin
- ERC-4361: Sign-In with Ethereum — Ethereum Improvement Proposals (Wayne Chang, Jack Tanner et al., October 2021)
- BLS Signatures — Chialisp.com Official Documentation
- Signatures — Chia Documentation (docs.chia.net)
- Crypto Signatures and Why Chia Chose BLS — Chia Network Official Blog, November 2025
- Ed25519 Signature Verification in Solana — RareSkills, October 2025
- EIP-191 Message Signing — Coinbase Developer Platform Documentation
- BLS Signatures Library (BLS12-381) — Chia Network on GitHub
- Sign In With Ethereum — MetaMask Developer Documentation
