How to sign and verify using metamask

I need to concatenate a string (test1) and contract address (address(this). Here is my smart contract,

pragma solidity ^0.6.2;
pragma experimental ABIEncoderV2;

contract Sample {
    function getContractAddress() public view returns (address) {
        return address(this);
    }
    
    function verifySign(uint8 v, bytes32 r, bytes32 s) public view returns (address) {
        return ecrecover(keccak256(abi.encodePacked("test1", address(this))), v, r, s);
    }
}

Using the following code, I am signing message in metamask.

var account = window.ethereum.selectedAddress
var msg = window.web3.utils.sha3(web3.utils.toHex("test1") + "0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8", {encoding:"hex"})
var signature = await window.web3.eth.personal.sign(msg, account);
var r = signature.substr(0,66)
var s = "0x" + signature.substr(66,64)
var v = 28

But return value of verifySign in solidity contract is not same as account (address which is used to sign message)

Help to find out what I am missing here

You need to use ECDSA.toEthSignedMessageHash. See the first part of this workshop, where we explain how to sign and recover messages.

1 Like

As you said, I changed my contract like following,
pragma solidity ^0.6.2;
pragma experimental ABIEncoderV2;

contract Sample {
    function getContractAddress() public view returns (address) {
        return address(this);
    }
    
    function verifySign(uint8 v, bytes32 r, bytes32 s) public view returns (address) {
        return ecrecover(toEthSignedMessageHash(keccak256(abi.encodePacked("test1", address(this)))), v, r, s);
    }
    
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }    
}

But I am not getting the signer address.
code used to sigin,

var account = window.ethereum.selectedAddress
var msg = window.web3.utils.sha3(web3.utils.toHex("test1") + "0xd9145CCE52D386f254917e481eB44e9943F39138", {encoding:"hex"})
var signature = await window.web3.eth.personal.sign(msg, account);
var r = signature.substr(0,66)
var s = "0x" + signature.substr(66,64)
var v = 28

Hello @selv.

Can you tell us more about your issue signing? Is metamask not showing the popup? Is the resulting signature invalid?

AFAIK, the web method for signing messages is web3.eth.sign("message", walletAddress) as described in their doc. Have you tried this approach?

@Amxx Thank you very much for responding. Metamask pop is working and I can get signature too. But when verifying in smart contract I am not getting signer address which means output of verifySign method in solidity is not same as account variable in javascript are not same value. web3.eth.sign method is deprecated https://github.com/0xProject/0x-monorepo/issues/162
For more information I have attached images.


Hello again,

I’m having doubts about your message construction.
var msg = window.web3.utils.sha3(web3.utils.toHex("test1") + "0xd9145CCE52D386f254917e481eB44e9943F39138", {encoding:"hex"})

On the solidity side, you message is a string that starts with test, and ends with the string that is the ascii representation of the address (likely to contain non human readable characters)

On the javascript side, you are concatenating things in a weird way, with a 0x that is not trimmed out …

I’ll suggest you start with signing JUST “test” … without the address … and you work you way from up from this