Basically on verifying the signature with the generated hash of the signed message, I'm getting the correct signer on JS, but not on Solidity.
JS Code (According to Keir Finlow-Bates' suggestion):
async function signHello() { // Encode parameters const encodedParams = ethers.utils.defaultAbiCoder.encode( ['string'], ['hello'] ); const hash = ethers.utils.hexlify(ethers.utils.toUtf8Bytes(encodedParams)); const prefixedHash = ethers.utils.solidityKeccak256( ['string', 'bytes'], ['\x19Ethereum Signed Message:\n' + hash.length, hash] ); console.log("Hash: ", prefixedHash) // Sign the message const signature = await wallet.signMessage(ethers.utils.arrayify(prefixedHash)); console.log("Signature:", signature); return {'hash': prefixedHash, 'signature': signature} } function getSigner(hash, signature) { const digest = ethers.utils.keccak256(ethers.utils.solidityPack(['string', 'bytes32'], ["\x19Ethereum Signed Message:\n32", hash])); return ethers.utils.recoverAddress(digest, signature); } signHello().then((res) => { console.log(getSigner(res.hash, res.signature)) // getting the correct signer address here }) JS Code:
const ethers = require('ethers'); const provider = new ethers.providers.JsonRpcProvider("https://polygon-mumbai.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY"); const privateKey = "YOUR_ACCOUNT_PRIVATE_KEY"; const wallet = new ethers.Wallet(privateKey, provider); async function signHello() { // Encode parameters const encodedParams = ethers.utils.defaultAbiCoder.encode( ['string'], ['hello'] ); let hash = ethers.utils.keccak256(encodedParams); console.log("Hash: ", hash) // Sign the message const signature = await wallet.signMessage(hash); console.log("Signature:", signature); return {'hash': hash, 'signature': signature} } function getSigner(hash, signature) { return ethers.utils.verifyMessage(hash, signature) // Using the code below, getting the same incorrect signer address as getting from the getSigner() solidity function // const digest = ethers.utils.keccak256(ethers.utils.solidityPack(['string', 'bytes32'], ["\x19Ethereum Signed Message:\n32", hash])); // return ethers.utils.recoverAddress(digest, signature); } signHello().then((res) => { console.log(getSigner(res.hash, res.signature)) // getting the correct signer address here }) Solidity Code:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; contract Signer { function getHash() public pure returns (bytes32) { return keccak256( abi.encode( "hello" ) ); } function getSigner(bytes32 _hash, bytes memory _signature) public pure returns (address) { bytes32 r; bytes32 s; uint8 v; if (_signature.length != 65) { return address(0); } assembly { r := mload(add(_signature, 32)) s := mload(add(_signature, 64)) v := byte(0, mload(add(_signature, 96))) } if (v < 27) { v += 27; } if (v != 27 && v != 28) { return address(0); } else { return ecrecover( keccak256( abi.encodePacked( "\x19Ethereum Signed Message:\n32", _hash ) ), v, r, s ); } } function getSignerUsingOpenzeppelin(bytes32 _hash, bytes memory _signature) public pure returns (address) { return ECDSA.recover(_hash, _signature); } } PS, I'm getting the same hash for hello in both JS as well as Solidity codes, i.e., 0x984002fcc0ca639f96622add24c2edd2fe72c65e71ca3faa243e091e0bc7cdab.
Using the JS Code (According to Keir Finlow-Bates' suggestion), getting this hash:
0xd9f807e25c27377c0d87443b1736dfaa5c3a582d7023b696acf4dde098ee659e
Which is when used with the corresponding generated signature, returning the correct/expected signer.
But, now the question is how to generate the same hash in solidity?
As keccak256(abi.encode("hello")) is returning a different hash i.e., 0x984002fcc0ca639f96622add24c2edd2fe72c65e71ca3faa243e091e0bc7cdab.
return ECDSA.recover(_hash, _signature);