Solidity – Call Ethereum Smart Contract Function Using Web3.js 1.2.6

ethereumjssolidityweb3js

I am trying to call a smart contract but i am getting error : The function eth_sendTransaction does not exist. I think it is because of transaction not signed , but how can i sign the transaction when i am calling .send() method. Please tell me what am i doing wrong here?

My smart contract function does not accept any argument but when executed it changes a bool value so i am assuming it is changing the state of contract. So can anyone please guide me how to call functions to alter contract data and also transaction to get data.

    const sourceCode = fs.readFileSync('contract_path', 'utf8').toString();
    const compiledCode = compiler.compile(sourceCode, 1).contracts[':Contract_Name']
    const abi = JSON.parse(compiledCode.interface);
    const bin = compiledCode.bytecode;
    var contractInstance = new web3.eth.Contract(abi, data.contractAddr);
    // build the transaction 
    web3.eth.getTransactionCount(data.signerPubKey, (err, txCount) => {
        if (txCount) {            
            // using the callback
            contractInstance.methods.signAgreement().send({ nonce: web3.utils.toHex(txCount), from: 'my_public_key', gasLimit: web3.utils.toHex('2100000'), gasPrice: web3.utils.toHex(web3.utils.toWei('10', 'gwei')) }, function (error, transactionHash) {
                if (error) {
                    next(error, null)
                } else if (transactionHash){
                    next(null, transactionHash)
                }
            });
        } else if (err && err.message) {
            next(err.message, null);
        }
    })

Best Answer

Sending a transaction without signing it first is supported only if you unlock your account on the node that you're communicating with beforehand (for example, when you run your tests on Ganache).

On public nodes, this is obviously not feasible (neither would you want it to be, of course).

You'll need to sign the transaction first, and then send the signed-transaction instead.

For example (tested with web3.js v1.2.1):

const Web3 = require("web3");

const NODE_ADDRESS = "YourNodeAddress";
const PRIVATE_KEY  = "0xYourPrivateKey";

async function scan(message) {
    process.stdout.write(message);
    return await new Promise(function(resolve, reject) {
        process.stdin.resume();
        process.stdin.once("data", function(data) {
            process.stdin.pause();
            resolve(data.toString().trim());
        });
    });
}

async function getGasPrice(web3) {
    while (true) {
        const nodeGasPrice = await web3.eth.getGasPrice();
        const userGasPrice = await scan(`Enter gas-price or leave empty to use ${nodeGasPrice}: `);
        if (/^\d+$/.test(userGasPrice))
            return userGasPrice;
        if (userGasPrice == "")
            return nodeGasPrice;
        console.log("Illegal gas-price");
    }
}

async function getTransactionReceipt(web3) {
    while (true) {
        const hash = await scan("Enter transaction-hash or leave empty to retry: ");
        if (/^0x([0-9A-Fa-f]{64})$/.test(hash)) {
            const receipt = await web3.eth.getTransactionReceipt(hash);
            if (receipt)
                return receipt;
            console.log("Invalid transaction-hash");
        }
        else if (hash) {
            console.log("Illegal transaction-hash");
        }
        else {
            return null;
        }
    }
}

async function send(web3, account, transaction) {
    while (true) {
        try {
            const options = {
                to      : transaction._parent._address,
                data    : transaction.encodeABI(),
                gas     : await transaction.estimateGas({from: account.address}),
                gasPrice: await getGasPrice(web3),
            };
            const signed  = await web3.eth.accounts.signTransaction(options, account.privateKey);
            const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction);
            return receipt;
        }
        catch (error) {
            console.log(error.message);
            const receipt = await getTransactionReceipt(web3);
            if (receipt)
                return receipt;
        }
    }
}

async function run() {
    const web3        = new Web3(NODE_ADDRESS);
    const account     = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
    const contract    = new web3.eth.Contract(abi, data.contractAddr);
    const transaction = contract.methods.signAgreement();
    const receipt     = await send(web3, account, transaction);
    console.log(JSON.stringify(receipt, null, 4));
    if (web3.currentProvider.constructor.name == "WebsocketProvider")
        web3.currentProvider.connection.close();
}

run();
Related Topic