0

This is probably a very simple problem to solve, but I am setting up a node.js backend using express for my Dapp and I can now deploy new contracts just fine.

I now need to figure out how to make calls to a function in an existing contract (created previously in Remix). I have the following syntax for setting up the provider. This works, at least for creating a new Insurer:

const Web3 = require("web3");
const HDWalletProvider = require("@truffle/hdwallet-provider");

require('dotenv').config();
const provider = new HDWalletProvider(process.env.MNEMONIC, process.env.INFURA_URL);

var contract    = require("@truffle/contract");

const insurerArtifacts = require('./build/contracts/Insurer.json');
var myInsurerContract = contract(insurerArtifacts);
myInsurerContract.setProvider(provider);

const platformArtifacts = require('./build/contracts/Platform.json');
var myPlatformContract = contract(platformArtifacts);
myPlatformContract.setProvider(provider);

The function call I make is as follows:

var insurerAddress = '0x...';
var myClientUIC = req.body.clientUIC;
var myClientName = req.body.clientName;
var myClientGroup = req.body.clientGroup;
var myClientNACE = req.body.clientNACE;
var myCreditLine = parseInt(req.body.creditLine);
var myCreditLineDate = parseInt(req.body.creditLineDate);
var myClientCurrency = req.body.clientCurrency;
var myClientCity = req.body.clientCity;
var myClientCountry = req.body.clientCountry;

myPlatformContract.at("0x...").methods.createClient(myClientUIC, myClientName, myClientGroup, 
    myClientNACE, insurerAddress, myCreditLine, myCreditLineDate, myClientCurrency, myClientCity, myClientCountry,
    { from:provider.getAddress() })
    .once('transactionHash', function(hash) {
        console.log("TxHash: " + hash);
    }).on('receipt', function(receipt) { console.log("receipt: " + JSON.stringify(receipt)); })
        .then(function(result) {
    }, function(error) {
        console.log(error);
    });

The error I get is:

TypeError: Cannot read property 'createClient' of undefined 

However, createClient is a function and I have checked that all the parameters have values. Does createClient itself need to be called differently?

I would appreciate any help you an give me with this beginner's issue! Many thanks. Phil

  • What version of web3.js are you using? Possibly need to add .methods before .createClient . – goodvibration Jan 29 at 0:31
  • My web3 is version 1.2.5. I have added the call methods and I now get a different error. The error I get is "Cannot read property 'createClient' of undefined". Is there a problem with the way I reference the .at function? I will edit the main question to reflect the change. Thanks! – Philip Rutovitz Jan 29 at 6:18
  • Could you try this and show whether you are able to get contract address. const instance = myPlatformContract.at(contractAddress) and console.log(instance.address) . This issue comes when your contract is not deployed properly or you're calling wrong abi – Jaydeep Chauhan Jan 29 at 7:10
  • @jadd22 - thank-you, that is definitely the problem. console.log shows undefined. That is weird though as when I go to remix for example, the contract very much exists and I can make calls to it. What am I missing? – Philip Rutovitz Jan 29 at 7:22
  • @PhilipRutovitz How did you deploy your code.Is it on testnet or ganache?.Can you share the code where you setting provider?. – Jaydeep Chauhan Jan 29 at 7:53
1

I believe you have to await for the contract instance to be returned before calling any of its methods. That's why you're getting undefined.

So, you should first do this:

_instance = await myPlatformContract.at("0x...");

And then you can go ahead and call methods of it. Put it inside an async function, so that you can use await.

async function createAndCall() {
    console.log("instantiating contract...");

    _instance = await myPlatformContract.at(deployedAddress);

    console.log("fetching response...")

    //I'm using your original contract to test this and I know it has the 
    //insurerUIC public variable; this line is here just to show the instance
    //is now available and working:
    let response = await _instance.insurerUIC.call(); 

    console.log("Response: " + response);
}

The best way to do this is by creating a variable that holds the instance. So, what I usually do is something that resembles this:

var _instance;
async function instantiate() {
    _instance = await myContract.at(deployedAddress);
}

And then after that resolves I just call functions normally. You don't need the .methods part unless you want to call a function by its signature (which you don't right now). You can do it directly: _instance.functionName(params, {from:userAddress }).once(...

The important thing to remember here is that both calls and transactions are asynchronous, so you need to wait for the response before doing anything. This also applies to contract.at(), because it will verify if code exists at the specified address before returning the instance.

| improve this answer | |

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.