Skip to content

Add EIP-191 personal message (personal_sign) support#265

Merged
alisinabh merged 1 commit into
mainfrom
feat/eip-191-personal-message
Jul 19, 2026
Merged

Add EIP-191 personal message (personal_sign) support#265
alisinabh merged 1 commit into
mainfrom
feat/eip-191-personal-message

Conversation

@alisinabh

@alisinabh alisinabh commented Jul 19, 2026

Copy link
Copy Markdown
Member

First PR of the v0.7 "Auth" tier (stacked chain: this → #266 universal verification → #267 SIWE).

What

  • New Ethers.PersonalMessage module: hash/1 (EIP-191 version 0x45), recover/2, recover!/2, and EOA-only verify/3 (case-insensitive address compare, checksummed output).
  • New optional signer callback personal_sign/2 on Ethers.Signer — named after the RPC method it mirrors — implemented for:
    • Ethers.Signer.Local (secp256k1 sign over the EIP-191 hash, v ∈ {27, 28})
    • Ethers.Signer.JsonRPC (delegates to personal_sign)
  • Top-level Ethers.personal_sign/2 and personal_sign!/2, mirroring sign_typed_data/2.

Design notes

  • Naming: personal_sign (rather than ethers.js/viem-style sign_message) is unambiguous about the signing scheme and matches the module (PersonalMessage) and the underlying RPC method; it also leaves clean room for future sign_* callbacks (e.g. EIP-7702 sign_authorization).
  • Messages are plain binaries treated as raw bytes; a "0x..." string signs as literal text (documented) — matching ethers.js signMessage string semantics. No {:raw, ...} variant.
  • Signatures accepted as 0x-hex or raw 65 bytes, v ∈ {0, 1, 27, 28}.

Testing

  • TDD; 100% coverage on new/changed lines including error branches.
  • Vectors ground-truthed against foundry (cast wallet sign / cast hash-message) across 2 keys and 4 message shapes (ASCII, empty, multibyte UTF-8, raw bytes), plus a live byte-for-byte Local↔JsonRPC cross-check against anvil's personal_sign.
  • Full suite green; mix format, mix credo --strict, mix dialyzer clean.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces first-class support for EIP-191 “personal message” signing (personal_sign) across the Ethers API surface (hashing/recovery/verification utilities plus signer integrations), enabling common wallet-login/auth flows.

Changes:

  • Add Ethers.PersonalMessage for EIP-191 hashing, signer recovery, and signature verification.
  • Add optional sign_message/2 callback to Ethers.Signer, implemented by Ethers.Signer.Local and Ethers.Signer.JsonRPC (via personal_sign).
  • Add top-level Ethers.sign_message/2 and sign_message!/2, plus comprehensive tests and changelog entry.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/ethers/signer/local_test.exs Adds Local signer sign_message/2 test vectors and JsonRPC parity check.
test/ethers/signer/json_rpc_test.exs Adds JsonRPC signer personal_sign coverage and verification assertions.
test/ethers/personal_message_test.exs New end-to-end unit tests for hashing, recovery, verification, and Ethers.sign_message/*.
lib/ethers/signer/local.ex Implements sign_message/2 for local secp256k1 signing over the EIP-191 digest.
lib/ethers/signer/json_rpc.ex Implements sign_message/2 delegating to JSON-RPC personal_sign.
lib/ethers/signer.ex Extends signer behaviour with optional sign_message/2 callback and docs.
lib/ethers/personal_message.ex Adds EIP-191 personal message primitives (hash, recover, verify).
lib/ethers.ex Adds top-level sign_message/2 and sign_message!/2 routing through configured signer.
CHANGELOG.md Documents the new EIP-191 personal message support.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/ethers/personal_message.ex
Comment thread lib/ethers/personal_message.ex
Introduce Ethers.PersonalMessage with pure EIP-191 (version 0x45)
primitives — hash/1, recover/2, recover!/2 and verify/3 — plus a new
optional sign_message/2 signer callback implemented by the Local signer
(secp256k1) and the JsonRPC signer (personal_sign), exposed through
Ethers.sign_message/2 and Ethers.sign_message!/2.

Messages are treated as raw bytes exactly as given; 0x-prefixed strings
are signed as literal text, matching ethers.js string semantics. All
vectors are cross-checked against foundry's cast and anvil.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alisinabh
alisinabh force-pushed the feat/eip-191-personal-message branch from adbc89c to 4ed65a5 Compare July 19, 2026 04:29
@alisinabh
alisinabh merged commit 271c39d into main Jul 19, 2026
5 checks passed
@alisinabh
alisinabh deleted the feat/eip-191-personal-message branch July 19, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants