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.