I almost gave up on my project.
I was building a P2P NFT swap on Hedera. Everything worked. But message signature verification kept failing.
The Problem
// Frontend
const signature = await wallet.signMessage("Sign in to MichiMint");
// Backend
const isValid = pubKey.verify(
Buffer.from("Sign in to MichiMint"),
signature
);
// false β
Same message. Fresh signature. Correct public key. Why?
The Solution
Hedera wallets add a hidden prefix:
function prefixMessage(message: string): string {
return '\x19Hedera Signed Message:\n' + message.length + message;
}
// Backend verification
const prefixedMessage = prefixMessage("Sign in to MichiMint");
const isValid = pubKey.verify(Buffer.from(prefixedMessage), signature);
// true β
Breaking it down:
-
\x19- Magic byte (prevents TX collision) -
Hedera Signed Message:\n- Context identifier -
22- Message length -
Sign in to MichiMint- Your message
Why This Isn't Documented
Hedera adopted Ethereum's EIP-191 standard. They assumed devs would know.
I couldn't find this in official docs, HashPack docs, or Stack Overflow. Only one Reddit comment.
Why It Matters
Without the prefix, signatures could be replayed in different contexts. The prefix makes them context-specific and secure.
This powers MichiMint - first trustless P2P NFT swap on Hedera using Scheduled Transactions.
If this saved you debugging time, share it. The next dev shouldn't have to hunt through Reddit.
Top comments (0)