I use these contract functions to verify signed message:
function verify( address to, uint256 amount, uint256 nonce, bytes memory signature ) public view returns (bool) { address from = owner(); console.log("from contract"); bytes32 messageHash = getMessageHash(from, to, amount, nonce); console.logBytes32(messageHash); bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash); console.logBytes32(ethSignedMessageHash); address addr = recoverSigner(ethSignedMessageHash, signature); console.log(addr); return recoverSigner(ethSignedMessageHash, signature) == owner(); } function getMessageHash( address from, address to, uint256 amount, uint256 nonce ) public pure returns (bytes32) { return keccak256(abi.encode(from, to, amount, nonce)); } function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) { return keccak256( abi.encodePacked( "x19Ethereum Signed Message:n32", _messageHash ) ); } function splitSignature(bytes memory sig) public view returns ( uint8, bytes32, bytes32 ) { require(sig.length == 65, "Invalid signature length"); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } console.logUint(v); console.logBytes32(r); console.logBytes32(s); return (v, r, s); } function recoverSigner( bytes32 _ethSignedMessageHash, bytes memory _signature ) public view returns (address) { (uint8 v, bytes32 r, bytes32 s) = splitSignature(_signature); return ecrecover(_ethSignedMessageHash, v, r, s); }
But when I call it from js, it always gives me "Wrong signature" error, which is require()
that checks verify()
is equals to true.
To check contract functions I created js script, that mimic contract functionality with ethers.js:
async function info(signer, from, to, amount, nonce) { const payload = ethers.utils.defaultAbiCoder.encode( ["address", "address", "uint256", "uint256"], [from, to, amount, nonce] ) const array = ethers.utils.arrayify(payload) const hashed_payload = ethers.utils.keccak256(array) // message hash in contract console.log("Message Hash: ", hashed_payload) const types = ethers.utils.solidityKeccak256( ["string", "bytes32"], ["x19Ethereum Signed Message:n32", hashed_payload] ) console.log("Eth signed message: ", types) // eth signed message const signature = await signer.signMessage(array); console.log("Signature: ", signature) const sig = ethers.utils.splitSignature(signature); console.log("Signature:", sig); console.log("Recovered:", ethers.utils.verifyMessage(ethers.utils.arrayify(payload), sig)); console.log("++++")
The problem is that information from hardhat console and from js script is identical. Here it is:
// from js: Message Hash: 0xe0ad70fdc7e7a60160b483c98583ee4a9ed173709bab8e802628279ce64749c8 Eth signed message: 0x030deb535a9f28fcf9d3cc003d7e1889e793f1e53c9abde29b8a6c3a8e04b065 Signature: 0xd7e114deb528f8ca0d0926865febaaf9cf10583f694a9b92aac4abe33a92a7334b441c3c9936767f58d537cc6ab46c6cd5bc92c244afc5522385d3b8f22bc0ee1c Signature: { r: '0xd7e114deb528f8ca0d0926865febaaf9cf10583f694a9b92aac4abe33a92a733', s: '0x4b441c3c9936767f58d537cc6ab46c6cd5bc92c244afc5522385d3b8f22bc0ee', _vs: '0xcb441c3c9936767f58d537cc6ab46c6cd5bc92c244afc5522385d3b8f22bc0ee', recoveryParam: 1, v: 28, yParityAndS: '0xcb441c3c9936767f58d537cc6ab46c6cd5bc92c244afc5522385d3b8f22bc0ee', compact: '0xd7e114deb528f8ca0d0926865febaaf9cf10583f694a9b92aac4abe33a92a733cb441c3c9936767f58d537cc6ab46c6cd5bc92c244afc5522385d3b8f22bc0ee' } Recovered: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 ++++ from contract 0xe0ad70fdc7e7a60160b483c98583ee4a9ed173709bab8e802628279ce64749c8 // message hash 0x030deb535a9f28fcf9d3cc003d7e1889e793f1e53c9abde29b8a6c3a8e04b065 // eth signed message 28 // v 0xd7e114deb528f8ca0d0926865febaaf9cf10583f694a9b92aac4abe33a92a733 // r 0x4b441c3c9936767f58d537cc6ab46c6cd5bc92c244afc5522385d3b8f22bc0ee // s 0x63087790b2ffbb189b8e36508ed97f1749500432 // recovered address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 // address correctly recovered in js, so u can see, they are not equal
With this outputs and code it seems like recoverSigner()
with ecrecover()
and ethers.utils.verifyMessage()
works differently, but this is unlikely. I’m almost sure, there’s mistake I just can’t see.
Please, help.