Solidity Sign off-chain & Verify on-chain

This is a project which signs message off-chain and verifies signature on-chain inside Solidity code.

Prerequisites

Project setup

npm install

Compiles and hot-reloads for development

npm run serve

Now, you’re ready to open http://localhost:8080/ on Chrome browser, and you can run it step by step for a clearly understanding.

Compiles and minifies for production

npm run build

1. Connect With MetaMask

connectToMetamask: async function () {
    if (window.ethereum != undefined) {
        let accounts = await window.ethereum.enable();
        console.log("Your address: ", accounts);
        this.firstAccount = accounts[0]; 
    }
}

This will connect with MetaMask and get the account you selected from MetaMask Pop-up window.

2. Message hash

web3MessageHash: async function () {
    const info = this.getContractInfo();
    const rpcUrl = info[0]
    let web3 = new Web3(rpcUrl);
    
    const to = this.firstAccount;
    const _amount = 123 * 1e18;
    const _message = "Yidian Metaverse";
    const _nonce = 12;

    let result = web3.utils.soliditySha3(to, _amount, _message, _nonce);
    // or
    // let result = web3.utils.soliditySha3({t: 'address', v: to}, {t: 'uint256', v: _amount}, {t: 'string', v: _message}, {t: 'uint256', v: _nonce});
    
    this.messageHash = result;
}

This method will generate a hash string from the data you want to pack, it’s only done locally.

3. Sign message hash in use of your private key

MetaMaskSignedMsgHash: async function () {
    let result = await window.ethereum.request({ method: "personal_sign", params: [this.firstAccount, this.messageHash]});
    console.log("Signature=", result)
    this.signedMessageHash = result;
}

This method will generate data sigature by using MetaMask so that users know how do we use their private key.

4. Verify on Solidity

solVerify: async function () {
    const info = this.getContractInfo();
    const rpcUrl = info[0]
    const contractAddress = info[1]
    const contractAbi = info[2]

    const web3 = new Web3(rpcUrl);

    const to = this.firstAccount;
    const _amount = web3.utils.toHex(123 * 1e18);
    const _message = "Yidian Metaverse";
    const _nonce = 12;

    const myContract = new web3.eth.Contract(contractAbi, contractAddress);
    const result = await myContract.methods.verify(this.firstAccount, to, _amount, _message, _nonce, this.signedMessageHash).call();
    console.log("The Verification result:", result);
}

This is the PRC endpoint、contract address、and contract abi for the signature verification scenario.

getContractInfo: function () {
    const rpcUrl = "https://rinkeby.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161";
    const contractAddress = "0xff0cC28F685b74AB46F724844B49Ff3E71eF65EC";
    
    const contractAbi = [{"inputs":[{"internalType":"bytes32","name":"_messageHash","type":"bytes32"}],"name":"getEthSignedMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_message","type":"string"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_ethSignedMessageHash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"recoverSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"sig","type":"bytes"}],"name":"splitSignature","outputs":[{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_message","type":"string"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}];
    return [rpcUrl, contractAddress, contractAbi];
} 

GitHub

View Github