EIPs

EIP-191 — Signed Data Standard

eips.ethereum.org/EIPS/eip-191

EIP-191 defines how arbitrary data should be hashed before signing so that signatures can never be reused as transactions. The common variant is 0x45 (personal_sign): prefix "\x19Ethereum Signed Message:\n" || len(message) || message, then keccak.

import { personal_sign, build_personal_message } from "@ethernauta/eip/191";

// path 1 — through a wallet
const signature = await personal_sign({
  message: "Hello, world",
  account: address,
})(signer({ chain_id: eip155_1.chain_id }));

Surface

ExportTypePurpose
personal_signSignable<Bytes65>RPC binding for personal_sign.
build_personal_message(message: string) => Uint8ArrayCompose the \x19... byte sequence.
build_personal_message_hex(message: string) => BytesHex version of the above.

Verifying

The signature can be verified off-chain via ECDSA recovery, or on-chain via EIP-1271:

import { verify_message } from "@ethernauta/crypto";

const ok = await verify_message({
  address,
  message: "Hello, world",
  signature,
})(reader({ chain_id: eip155_1.chain_id }));

verify_message tries EOA recovery first; if the address has code, it falls through to EIP-1271. verify_message_universal adds EIP-6492 for counterfactual accounts.

Why prefix the message

The prefix’s job is to make the resulting digest unmistakable for a transaction digest. Without it, a signed personal message could potentially be replayed as part of a transaction the user never intended.

The literal prefix bytes are 0x19 || 0x45 || "thereum Signed Message:\n" || len(message). The leading 0x19 byte is the EIP-191 identifier; 0x45 is the variant for personal_sign. EIP-712 uses the same 0x19 lead with a different variant.

See also