# What is ecrecover?

By [Azriel](https://paragraph.com/@goktus) · 2025-07-19

---

### Introduction

In blockchain and Web3 technologies, secure communication and identity verification are made possible by digital signatures. On Ethereum, one of the core tools for verifying these signatures is the built-in Solidity function `ecrecover`.

In this post, we’ll explore what `ecrecover` is, why we need the `v`, `r`, and `s` parameters, what they represent, and how digital signature verification works. We'll also walk through practical examples of both signing and verification.

* * *

### Digital Signatures and Ethereum's Signing Process

Ethereum uses the **ECDSA** (Elliptic Curve Digital Signature Algorithm) over the **secp256k1** curve to ensure the security of messages and transactions. Here's how it works:

*   A user signs the hash of a message with their private key.
    
*   The result is a digital signature composed of three parts:
    
    *   `r`: A 32-byte value generated during the signing process.
        
    *   `s`: Another 32-byte value.
        
    *   `v`: A 1-byte recovery identifier used to determine the correct public key from the signature. In Ethereum, this is typically `27` or `28`.
        

Together, these three values prove that the message was signed by someone in possession of the corresponding private key.

* * *

### The ecrecover Function and Its Parameters

In Solidity, the `ecrecover` function is used as follows:

    address signer = ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s);
    

*   `hash`: The `keccak256` hash of the signed message.
    
*   `v`, `r`, and `s`: The three components of the digital signature.
    

This function returns the Ethereum address that created the signature, allowing you to verify the authenticity of the signer directly onchain.

* * *

### Technical Breakdown of `v`, `r`, and `s`

*   **r:** First 32-byte value output from the ECDSA signing process.
    
*   **s:** Second 32-byte value of the signature.
    
*   **v:** Recovery parameter, helps choose the correct public key. Usually 27 or 28 in Ethereum.
    

The `v` value is necessary because the same `r` and `s` can produce two possible public keys. `v` tells us which one is valid.

* * *

### Practical Example: Signing a Message in TypeScript

    import { ethers } from "ethers";
    
    async function signMessage() {
      const wallet = ethers.Wallet.createRandom(); 
      const message = "Hello Ethereum!"; 
      const signature = await wallet.signMessage(message); 
      const sig = ethers.Signature.from(signature); 
    
      console.log("Address:", wallet.address); 
      console.log("Message:", message); 
      console.log("v:", sig.v); 
      console.log("r:", sig.r); 
      console.log("s:", sig.s); 
    } 
    signMessage();
    

This script creates a random wallet, signs a message, and logs the `v`, `r`, and `s` values to the console.

* * *

### Practical Example: Verifying a Signature in Solidity

    pragma solidity ^0.8.0;
    
    contract VerifySignature {
      function verify(
                bytes32 messageHash,
                uint8 v,
                bytes32 r,
                bytes32 s,
                address expectedSigner
             ) public pure returns (bool) { 
             return ecrecover(messageHash, v, r, s) == expectedSigner; 
             } 
    }
    

This contract takes the `messageHash` and signature components, recovers the signer’s address, and checks if it matches the expected address.

* * *

![Ecrecover Diagram](https://storage.googleapis.com/papyrus_images/b9414f176f6c0d45dfa2b619a39aa232470e7a391b41ce13a0996ec1955060ec.png)

Ecrecover Diagram

### Ecrecover in Zero-Knowledge Circuits

Using `ecrecover` directly inside zero-knowledge proof (ZKP) circuits is computationally expensive. Instead, most ZK circuits accept the signer’s **public key** (split into `x` and `y` coordinates) as an input.

Here’s an example in Noir:

    fn main(
      pub_key_x: [u8; 32],
      pub_key_y: [u8; 32],
      signature: [u8; 64],
      hashed_message: pub [u8; 32]
    ) -> pub Field {
      let address = ecrecover::ecrecover(pub_key_x, pub_key_y, signature, hashed_message);
    address 
    }
    

This approach avoids the cost of recovering the public key from the signature and makes the circuit more efficient.

* * *

### Post-EIP-155: How `v` Changes

EIP-155 introduced a modification to the `v` parameter to include the `chainId` as a protection against replay attacks.

The formula is:

    v = CHAIN_ID * 2 + 35 or CHAIN_ID * 2 + 36
    

Examples:

*   On Ethereum Mainnet (`chainId = 1`), possible `v` values are `37` and `38`.
    
*   On Polygon (`chainId = 137`), possible values are `309` and `310`.
    

This ensures that the same signature cannot be reused across multiple chains.

* * *

### Security Notes & Tips

*   While `v` is typically `27` or `28`, EIP-155 allows it to take other values.
    
*   Always validate the `v` value correctly during signature verification.
    
*   Always check that the address returned by `ecrecover` matches the expected signer.
    
*   To prevent replay attacks, include a `nonce` or `chainId` in the message being signed.
    

* * *

### Conclusion

Ethereum’s `ecrecover` function is a foundational tool for verifying digital signatures on-chain. Understanding how it works including the role of `v`, `r`, and `s` is critical for writing secure smart contracts.

As zero-knowledge technologies evolve, verifying signatures efficiently off-chain and inside ZK circuits becomes more practical but at the core, it all starts with understanding how signature recovery works.

---

*Originally published on [Azriel](https://paragraph.com/@goktus/what-is-ecrecover)*
