I'm trying to get the hang of Web3 and Solidity for an upcoming project. So for now I am just fiddling around with some different things, and I have gotten myself stuck on verifying signatures. I am able to sign, but I cannot verify. I have been trying for 3-4 hours every night for the past two nights now, but I am simply unable to figure out what the issue is, so now I come crawling here for help.
I have included both my clientside and contract code here. I hope someone can point me in the right direction. I do get a result back from the call to verify
on the contract, but the result is always false
.
The contract code is more or less copy/pasted from https://solidity-by-example.org/signature/, which I am aware is a bit outdated.
All addresses and keys in the example below are from testnet.
Clientside, using MetaMask:
const contractAddress = '0x94F9f9d16f2cb8542BE31D6fA99471Ea8251D7eC';
const sign = async (address) => {
const nonce = Math.floor(Math.random() * 10000000000) + 1;
console.log(nonce);
const data = [{"address": address}, {"uint256": 1000000}, {"uint256": nonce}, {"address": contractAddress}];
console.log(window.web3.eth.accounts.sign(generateHash(data), '61323f1a2911e70573be49819e68a7dbffa10216135bb06171e596a4f89bfd79'));
}
const verify = async (address, nonce, signature) => {
const contractInstance = new window.web3.eth.Contract(
ABI[0].abi,
contractAddress,
{
from: address,
}
);
const result = contractInstance.methods.verify(contractAddress, address, 1000000, nonce, signature).call(function (error, result) {
console.log(result);
});
}
const generateHash = (data) => {
const keys = data.map((item) => {
return Object.keys(item)[0];
});
const values = data.map((item) => {
return Object.values(item)[0];
});
return "0x" + ethereumjs.soliditySHA3(keys, values).toString("hex");
}
//sign('0x93E16885EE732BFF281285efe2f2F46Dc92590C5');
verify('0x93E16885EE732BFF281285efe2f2F46Dc92590C5', 3421724946, '0x7eba658b197add3604c7d25c9305507cc67dd7a9298862fb4fcf2a2bcfd274d6039681008fa7a6201194258dee17eed4d709ea704427135cb80731a44908d29a1b');
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import '@openzeppelin/contracts/access/Ownable.sol';
contract Moonboi is ERC20, Ownable {
constructor() ERC20("Moonboi Token", "MBT") {}
function getMessageHash(
address _to, uint _amount, uint _nonce
) public view returns (bytes32) {
return keccak256(abi.encodePacked(_to, _amount, _nonce));
}
function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash));
}
function verify(
address _signer,
address _to, uint256 _amount, uint256 _nonce,
bytes memory signature
) public view returns (bool) {
bytes32 messageHash = getMessageHash(_to, _amount, _nonce);
bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
return recoverSigner(ethSignedMessageHash, signature) == _signer;
}
function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) {
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
return ecrecover(_ethSignedMessageHash, v, r, s);
}
function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) {
require(sig.length == 65, "invalid signature length");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
return (r, s, v);
}
}
Best Answer
I figured out what I was doing wrong, so I will add my findings here as an answer, in case someone else has made the same mistake. The problem is pretty obvious once I realized what was going on.
When calling the smart contract method
verify
I was passing in the smart contract address as the signer, when in reality the signer is the owner of the smart contract.So in this method:
.. the first parameter in the
verify
method should instead be the contract owner, like so:Another mistake I made was that the private key needs to be prefixed with
0x
, so:would not have worked. I should pass in
0x61323f1a2911e70573be49819e68a7dbffa10216135bb06171e596a4f89bfd79
instead.