EIP-191 Message Signing Integration

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
EIP-191 Message Signing Integration
Simple
from 1 business day to 3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1218
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    854
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1047
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    825

EIP-191 Integration (Message Signatures)

User has connected MetaMask, you want to verify they own the address without sending a transaction. Or you need to implement gasless whitelist: backend issues a signed permission, contract verifies it on-chain. Both cases — EIP-191.

What EIP-191 Does

EIP-191 standardizes message signature format in Ethereum. Without standard, signature of arbitrary bytes matches transaction signature — theoretical phishing vector. EIP-191 adds prefix \x19Ethereum Signed Message:\n{length} before hashing, making signatures specific to Ethereum and unreadable as transactions.

EIP-191 versions:

  • 0x45personal_sign (adds text prefix, human-readable in MetaMask)
  • 0x01 — structured data (this is EIP-712, EIP-191 extension)
  • 0x00 — validator data (less common)

Most cases — version 0x45: personal_sign in MetaMask shows user readable text, user understands what they're signing.

On-Chain Verification

In Solidity, verification via ecrecover:

function verify(string calldata message, bytes calldata signature) 
    public pure returns (address signer) 
{
    bytes32 messageHash = keccak256(bytes(message));
    bytes32 ethSignedHash = keccak256(
        abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)
    );
    return ECDSA.recover(ethSignedHash, signature);
}

OpenZeppelin ECDSA.recover — correct way: handles v = 27/28, protected against signature malleability (verifies s is in lower half of curve, per EIP-2).

Typical mistake: hash string directly via keccak256(abi.encodePacked(message)) without EIP-191 prefix. Signature from MetaMask (personal_sign) contains prefix — verification without it gives wrong signer.

Use Case: Gasless Whitelist via Backend Signature

Backend stores whitelist addresses. Instead of storing whitelist on-chain (expensive, requires transaction to add), backend signs permission for specific address. User presents signature to contract on mint:

function mint(bytes calldata signature) external {
    bytes32 hash = keccak256(abi.encodePacked(msg.sender, address(this)));
    bytes32 ethHash = MessageHashUtils.toEthSignedMessageHash(hash);
    address signer = ECDSA.recover(ethHash, signature);
    require(signer == trustedSigner, "Invalid signature");
    _mint(msg.sender, nextTokenId++);
}

Important to include address(this) in hash — protection against replay between contracts. Include block.chainid or chainId — protection against replay between networks. For one-time permissions — nonce of user in hash.

For more structured data (amount, deadline, specific token) — better to use EIP-712 — user sees each field in MetaMask, not just hash.

Frontend Integration

With viem:

const signature = await walletClient.signMessage({ message: "Verify ownership" });

With ethers.js:

const signature = await signer.signMessage("Verify ownership");

Both return 65-byte signature (r + s + v). Transmitted to contract as bytes.

Timeline

EIP-191 integration into existing contract (signature verification, replay protection) + frontend code — 1 working day. With backend service for issuing signatures — 2-3 days.