1

My smart contract is already deployed on Ropsten TestNet provided by Infura at 0x63865E0F065C7b865e1a2EE4d99C383A414Bec22 address. I want to sendTransaction using NodeJS(v.10.16.1) + Web3(v.1.2.6) + @truffle/contract(v.4.1.11). This is my NodeJS code -

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var session = require('express-session');

var Web3            = require('web3');
var contract        = require("@truffle/contract");
var path            = require('path');
var MyContractJSON  = require(path.join(__dirname, 'build/contracts/contract.json'));

var provider    = new Web3.providers.HttpProvider("https://ropsten.infura.io/v3/PROJECT_ID");

var MyContract = contract(MyContractJSON);
MyContract.setProvider(provider);
const contract_address = "0x63865E0F065C7b865e1a2EE4d99C383A414Bec22";


app.post("/register", function(req, res){
    var newUser = req.body.username;
    var account = req.body.account;
    req.session.currentUser = account;

    MyContract.at(contract_address).then(function(instance) {

        return instance.registerUser.sendTransaction(newUser, {from: account});

    }).then(function(result) {
        console.log(result);

        res.render("dashboard", {name: newUser, account: account});

    }).catch(function(error) {
        console.log(error);
        res.redirect("/");
    });
});

I am getting this ERROR on sendTransaction -

Error: Returned error: The method eth_sendTransaction does not exist/is not available\n    at Object.ErrorResponse (...\\node_modules\\web3-core-helpers\\src\\errors.js:29:16)\n    at ...\\node_modules\\web3-core-requestmanager\\src\\index.js:140:36\n    at XMLHttpRequest.request.onreadystatechange (...\\node_modules\\web3\\node_modules\\web3-providers-http\\src\\index.js:110:13)\n    at XMLHttpRequestEventTarget.dispatchEvent (...\\node_modules\\xhr2-cookies\\dist\\xml-http-request-event-target.js:34:22)\n    at XMLHttpRequest._setReadyState ...\\node_modules\\xhr2-cookies\\dist\\xml-http-request.js:208:14)\n    at XMLHttpRequest._onHttpResponseEnd (...\\node_modules\\xhr2-cookies\\dist\\xml-http-request.js:318:14)\n    at IncomingMessage.<anonymous> (...\\node_modules\\xhr2-cookies\\dist\\xml-http-request.js:289:61)\n    at IncomingMessage.emit (events.js:203:15)\n    at endReadableNT (_stream_readable.js:1145:12)\n    at process._tickCallback (internal/process/next_tick.js:63:19)'

I am using Metamask(v.7.7.8) and managing the account address via express-session. I know that Infura does not support sendTransaction but @truffle/contract does not support sendRawTransaction too. Please help me find an easier solution with @truffle/contract.

  • Why @truffle/contract and not web3.eth.Contract? – goodvibration Mar 15 at 19:40
  • Also, since we don't have the file build/contracts/contract.json from your local machine, and you haven't verified your contract on Etherscan, please post the ABI (or by the least, the declaration of function `registerUser, so that we can use it in order to create that ABI ourselves). – goodvibration Mar 15 at 19:50
  • @truffle/contract handles contract abstraction in a better way. It handles the gas values and from addresses by default which we need to manage in web3.eth.Contract. – surhud004 Mar 15 at 21:30
  • Please post the ABI, or by the least, the declaration of function registerUser, so that we can use it in order to create that ABI ourselves. We don't have your build/contracts/contract.json file! – goodvibration Mar 15 at 21:34
  • This is the contract - gist.github.com/surhud004/20c92666c35a373b7afef960374d8766 – surhud004 Mar 15 at 21:37
1

I have created and compiled a contract according to the one that you've posted in a comment.

I have deployed it to Ropsten, and then verified it on Etherscan.

As you can see on Etherscan, its byte-code contains the sequence 704f1b94, which is the function-selector of function registerUser(string).

And as you can see on Etherscan, the byte-code of your contract does not contain this function-selector, which means that your contract does not implement this function!

This by itself is a good enough reason for your transaction to revert.

A few technical notes:

  1. You can generate the function-selector above by taking the first 4 bytes (8 hexadecimal characters) of the printout of console.log(Web3.utils.keccak256("registerUser(string)"));.

  2. Here is the contract which I have compiled, deployed and verified on Etherscan:

pragma solidity 0.4.26;

contract MyContract {

    struct user {
        address id;
        string name;
    }

    uint internal totalUsers = 0;
    mapping(address => user) internal users;

    function registerUser(string memory name) public {
        user storage u = users[msg.sender];
        require(keccak256(abi.encodePacked((name))) != keccak256(abi.encodePacked((""))));
        if(msg.sender == u.id) {
            revert("already registered");
        } else {
            users[msg.sender] = user(msg.sender, name);
            totalUsers++;
        }
    }
}
  1. Here is the NodeJS script which I have used for testing the contract function:
const Web3 = require("web3");

const PROJECT_ID  = process.argv[2];
const PRIVATE_KEY = process.argv[3];

const ABI  = [{"type":"function","name":"registerUser","inputs":[{"name":"name","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable","constant":false,"payable":false}];
const ADDR = "0x6285F7CC0cD9D4E5DBB81353276DCc099fa9C6B9";

async function send(web3, account, transaction, value = 0) {
    const options = {
        to      : transaction._parent._address,
        data    : transaction.encodeABI(),
        gas     : await transaction.estimateGas({from: account.address}),
        gasPrice: await web3.eth.getGasPrice(),
        value   : value
    };
    const signed  = await web3.eth.accounts.signTransaction(options, account.privateKey);
    const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
    return receipt;
}

async function run() {
    const web3     = new Web3("https://ropsten.infura.io/v3/" + PROJECT_ID);
    const contract = new web3.eth.Contract(ABI, ADDR);
    const account  = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
    const receipt  = await send(web3, account, contract.methods.registerUser("myName"));
    console.log(receipt);
}

run();
| improve this answer | |
  • Is it a good idea to access PRIVATE_KEY and signTransaction on server side i.e. in NodeJS code? – surhud004 Mar 16 at 21:59
  • 1
    @surhud004: Maybe not, but that (security aspect) was not a part of my investigation here (nor a part of your question, so you may want to post it as a separate one). – goodvibration Mar 16 at 22:49
1

It seems you are using your contract from the backend. In that case you cannot use MetaMask because it runs in the browser.

You have these options:

  • Execute the transactions that make changes from the frontend. The UI has to connect to MetaMask and use it as provider for @truffle/contract.

  • Use your own wallet in the backend and configure truffle to use it as provider. For example you can use HDWallet Provider.

| improve this answer | |
  • Thanks a lot for the answer. I will be considering the first option. Also, Metamask does not ask for confirming the transactions as well. I will have to resolve that problem too. Any suggestions are appreciated. – surhud004 Mar 15 at 21:45
  • 1
    @surhud004 Follow MetaMask recommendations and it should work docs.metamask.io/guide/…. Make sure the code runs in the browser, if you don't get any request perhaps it is running in the backend server. – Ismael Mar 15 at 23:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.