[Ethereum] How to send a signed transaction using metamask and web3

infurametamaskropstensolidityweb3js

I'm stuck at sending a signed transaction to a Smart Contract deployed on Ropsten network.

As learning purposes, I created a 'Voting' smart contract. This contract includes 2 candidates we can vote for. A user can only sign once. The contract has been deployed and tested on Ropsten. It works great, if I use only Web3 (ie I'm providing manually my Private Key).

Now, I would like the user to identify himself, then sign the vote. Here I'm stuck.

Using Vue, I have 2 main methods:

Method 1: Enable metamask

this.provider = await detectEthereumProvider();

    if (this.provider) {

      const accounts = await this.provider.request(
         {
              method: 'eth_requestAccounts'
          }
      );

      this.account = accounts[0];

    } else {

      console.log('Please install MetaMask!');

    }

Method 2: Send a signed Vote (here I'm stuck) –> partial code

  const ropsten = 'https://ropsten.infura.io/v3/MY_KEY';

  const web3 = new Web3(ropsten);

  const contract = new web3.eth.Contract(
                        Election.abi, 
                        this.params.to
                        );
  [??????????????]

  try {

    const txHash = await this.provider.request({
      method: 'eth_sendTransaction',
      params: [
        {
          from: this.account,
          to: this.contractAddress,
          data: ??????????????
        }
      ],
    });

    console.log(txHash);

  } catch (error) {

    console.log(error);
    
  }
}

If I understand well, now I have to create data parameter which a Hex of my function to vote.

Here is the function to vote I would use

const vote = await contract.methods.vote(1).send({
               from: ...
               to: ...
               });

Here I'm lost, I don't know how to proceed properly.

Expected output:

  • First, the user enable Metamask,
  • Second, the use vote for the candidate 1 or 2
  • Before sending the transaction to Ropsten, Metamask requests to sign the transaction

For information, here my Election.sol smart contract :

pragma solidity >=0.4.21 <0.7.0;

contract Election {

    //Model Candidate
    struct Candidate {
        uint id;
        string name;
        uint voteCount;
    }

    mapping(uint => Candidate) public candidates;
    //store accounts that have voted
    mapping(address => bool) public voters;

    uint public candidatesCount;

    event votedEvent (
        uint indexed _candidateId
    );

    constructor() public {
        addCandidate("Barack Obama");
        addCandidate("Donald Trump");
    }

    function addCandidate(string memory _name) private {
        candidatesCount ++;
        candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
    }

    function vote(uint _candidateId) public {
        //not voted yet
        require(!voters[msg.sender]);

        //valid vandidate
        require(_candidateId > 0 && _candidateId <= candidatesCount);

        //record vote
        voters[msg.sender] = true;

        //update candidate vote count
        candidates[_candidateId].voteCount ++;

        //voted event
        emit votedEvent(_candidateId);
    }

}

Your help is more then welcome! Thanks in advance!

Best Answer

Since you created a contract instance, you can use it have Metamask format the transaction and send sign request to user.

Try replacing this:

const txHash = await this.provider.request({
  method: 'eth_sendTransaction',
  params: [
    {
      from: this.account,
      to: this.contractAddress,
      data: ??????????????
    }
  ],
})

With this:

contract.methods.vote(1).send({
           from: accounts[0]
           
           });

You should see a Metamask request to sign Tx.

Related Topic