ethers cubre todo lo que una aplicación necesita para comunicarse con Ethereum —proveedores, contratos, codificación ABI, ENS—, y sus clases Wallet y SigningKey son lo que usa la mayor parte del código JavaScript para firmar. La aritmética de curvas procede de @noble/curves; ethers 6.17.0 fija su versión 1.2.0.
wallet.signMessage() implementa personal_sign: antepone al mensaje \x19Ethereum Signed Message:\n y su longitud (EIP-191), calcula el hash con keccak-256 y devuelve 65 bytes de r ‖ s ‖ v con v = 27 o 28. La comprobación contrastó el resumen con una implementación independiente de keccak y la firma con la salida RFC 6979 de @noble/curves, así que la misma clave y el mismo mensaje dan siempre los mismos bytes. Para firmar un hash que ya tienes calculado —un resumen EIP-712, el hash de una transacción—, usa SigningKey.sign(), que recibe exactamente 32 bytes y no añade ningún prefijo; cualquier otra cosa falla con invalid digest length.
Con la entrada, ethers es flexible en la estructura y estricto en la forma. verifyMessage() recuperó al firmante a partir de v = 27/28, de v = 0/1 y de la forma compacta EIP-2098 de 64 bytes, pero rechazó la firma high-S reflejada con non-canonical s; use ._s, aunque es matemáticamente válida. Las firmas producidas por OpenSSL, un KMS en la nube o módulos de hardware que no normalizan s deben convertirse a low-S (s → n − s, invertir v) antes de que ethers las acepte; el verificador de firmas de Ethereum muestra en qué forma está una firma.
La mayoría de los errores de verificación en los inicios de sesión con billetera no tienen nada que ver con la curva: un mensaje que difiere en un salto de línea o en un espacio final, un ID de cadena distinto en un dominio EIP-712, o una billetera de contrato inteligente cuyas firmas necesitan ERC-1271 en lugar de ecrecover. Inspecciona los datos tipados con el inspector de EIP-712 y los mensajes de inicio de sesión completos con el verificador de SIWE.
El ejemplo firma un mensaje de inicio de sesión, recupera la dirección y muestra la forma compacta; compara las direcciones recuperadas, nunca los bytes de la firma, porque la misma firma tiene varias codificaciones válidas.