Execution reverted when calling a method of my contract in NodeJs
An exception is thrown on the line 106 because this condition is not met: _value <= allowance[_from][msg.sender]
You need to set the allowance
prior to calling the transferFrom()
function, most likely by calling the approve()
function.
Or if you want a simple transfer (not using the allowance), use the transfer()
function instead.
Also, call()
is used for reading the output of pure/view functions. For interacting with external/public functions (sending ethereum transactions), use .send({from: <senderAddress>})
.
Example from https://web3js.readthedocs.io/en/v1.3.4/web3-eth-contract.html#web3-eth-contract
contract.methods.somFunc().send({from: ....})
Related videos on Youtube
RRGT19
Updated on June 04, 2022Comments
-
RRGT19 almost 2 years
I'm trying to do my own token in Solidity and use Web3 to transfer a token from one account to another using
NodeJS/ExpressJS
.I have been using
Infura
withrinkeby
.I can call my method
balanceOf
, but I cannot calltransferFrom
Error:
Returned error: execution reverted
const express = require('express'); const app = express(); const web3 = require('web3'); const INFURA_BASE_URL = 'https://rinkeby.infura.io/v3/'; const INFURA_API_KEY = '........'; web3js = new web3(new web3.providers.HttpProvider(INFURA_BASE_URL + INFURA_API_KEY)); /* Sender & Receiver keys */ const SENDER_PUBLIC_KEY = '........'; const SENDER_PRIVATE_KEY = '.......'; const RECEIVER_PUBLIC_KEY = '......'; /* Contract ABI. */ const CONTRACT_ABI = [ { "constant": true, "inputs": [], "name": "name", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "approve", "outputs": [ { "name": "success", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "totalSupply", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_from", "type": "address" }, { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "name": "success", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "decimals", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_value", "type": "uint256" } ], "name": "burn", "outputs": [ { "name": "success", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_from", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "burnFrom", "outputs": [ { "name": "success", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "symbol", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "name": "success", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" }, { "name": "_extraData", "type": "bytes" } ], "name": "approveAndCall", "outputs": [ { "name": "success", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "address" }, { "name": "", "type": "address" } ], "name": "allowance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "inputs": [ { "name": "initialSupply", "type": "uint256" }, { "name": "tokenName", "type": "string" }, { "name": "tokenSymbol", "type": "string" } ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "_owner", "type": "address" }, { "indexed": true, "name": "_spender", "type": "address" }, { "indexed": false, "name": "_value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "from", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Burn", "type": "event" } ]; const CONTRACT_ABI_ADDRESS = '............'; /* A controller listening at: http://localhost:3000/send */ app.get('/send', async function (req, apiResponse) { // Creating contract object const contract = new web3js.eth.Contract(CONTRACT_ABI, CONTRACT_ABI_ADDRESS, {from: SENDER_PUBLIC_KEY}); // Check the balance (working good) await contract.methods.balanceOf(RECEIVER_PUBLIC_KEY) .call() .then(res => { const str = web3.utils.fromWei(res); console.log('balance: ', str); }) .catch(err => { console.log(err); }); // Set the allowance (working) await contract.methods.approve(SENDER_PUBLIC_KEY, 1) .call() .then(res => { console.log('approve: ', res); }) .catch(err => { console.log('Error [approve]', err); }); // Initiate a transfer (not working) await contract.methods.transferFrom(SENDER_PUBLIC_KEY, RECEIVER_PUBLIC_KEY, 1) .call() .then(res => { console.log('transferFrom: ', res); }) .catch(err => { console.log('Error [transferFrom]', err); }); }); app.listen(3000, () => { console.log(`Example app listening at http://localhost:3000`) })
My code in Solidity here.
I have been struggling for days without any progress. Cannot see where the issue is.
My goal is to transfer a token from one account to another one in NodeJS.
-
RRGT19 about 3 yearsThanks for your reply. I have tried to call
approve()
beforetransferFrom
, but I received the same error message. Check my question edited with the use ofapprove()
. Something about thesend()
, I'm not sure if I should use it on my three methods in my NodeJs code. Tried to use it only withtransferFrom
but received a different error. Can you give check again and give me another hand? -
Petr Hejda about 3 yearsYes, you should use the
send({from: ...})
instead of thecall()
... The naming is a bit unfortunate, but I'll try to simply describe:call()
in JS is used for read-only. Whenever you need to write data to the blockchain (using external or public function of a contract), you need to send an Ethereum transaction - which is done using thesend()
function in JS. -
RRGT19 about 3 yearsGot it. I have used
.send({from: SENDER_PUBLIC_KEY})
on my three calls, but thetransferFrom()
method returnsThe method eth_sendTransaction does not exist/is not available
. Weird since I don't have a method in my contract with that name. Do you know how can I workaround this new error? Thanks for all your help by the way. -
Petr Hejda about 3 yearsYou're on the right path.
eth_sendTransaction
is a JSON-RPC method of the node that you're calling (in your case some of Infura's nodes). Which means, the flow has gone through the JS code, generated Ethereum tx, submit the tx to the node, and now the node refuses it... This is already out of my expertise, but my guess is that you have some incorrect credentials connecting to the node (since Infura is widely-used provider and it's unlikely they would have restricted this). -
RRGT19 about 3 yearsApparently, Infura don't allow to send an unsigned transaction. A topic here. Seems that there is a lot of work that I need to do, sign, serialize and send. I'm not sure in what part of my code should I do this, I suppose it's before calling the
transferFrom()
method? I don't understand the correct steps and what need to be done first. -
Petr Hejda about 3 yearsIt should be enough to set the private key (or mnemonic phrase and key index) and pass it to web3 as the default account. See github.com/ChainSafe/web3.js/issues/1527#issuecomment-395987528 ... Web3 then should be able to sign the transaction with this key automatically.
-
RRGT19 about 3 yearsNice. We are in the right path now. I have tried it and received an error saying
gas is missing
so, I added it and now I receives the errorPlease pass numbers as string or BN objects to avoid precision errors
. I'm passing the gas asstring
so, don't understand the error. Could you check my code updated here at line 354 to 370? Maybe I'm missing something that I don't see. -
Petr Hejda about 3 yearsI'm guessing the "gas is missing" is related to the transactions sent by
send()
functions on lines 374, 385 and 395. Unfortunately I don't have the capacity to debug the whole thing right now, but I'm glad that we've solved the original issue and that you're making progress. -
RRGT19 about 3 yearsThanks for your help to fix the original issue. Now I am in the right path.