DEV Community

Maximiliano Santana
Maximiliano Santana

Posted on

The Undocumented Secret to Hedera Message Signature Verification

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 ❌
Enter fullscreen mode Exit fullscreen mode

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 βœ…
Enter fullscreen mode Exit fullscreen mode

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.

michimint.xyz

If this saved you debugging time, share it. The next dev shouldn't have to hunt through Reddit.

Top comments (0)