Ethers.js: Call functions in contract returns promise error

Created on 17 Nov 2020  路  12Comments  路  Source: ethers-io/ethers.js

Here is the code I used to call setValue and getValue in contract Hello.sol deployed with etherjs 5.0.14 on android/windows10:

 import { ContractFactory, ethers } from "ethers";

 let url = "http://localhost:8545";
 const provider = new ethers.providers.JsonRpcProvider(url);
 const signer = provider.getSigner(); //forJsonRPC

 let contract_at_address = new ethers.Contract(contract.address, abi, signer); //<<==contract.address is the deployed address. abi and signer were defined earlier.
 await contract_at_address.setValue(10); //<<setter
 let value = await contract_at_address.getValue(); //<<==value shall be 10
 console.log("value contract at address : ", value)

But it throws error:

[Sun Nov 15 2020 23:10:11.457]  WARN     Possible Unhandled Promise Rejection (id: 0):
Error: call revert exception (method="getValue()", errorSignature=null, errorArgs=[null], reason=null, code=CALL_EXCEPTION,   version=abi/5.0.5)
makeError@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:110810:32
throwError@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:110820:31
http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:118492:68
step@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:118174:29
fulfilled@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:118058:34
tryCallOne@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:26991:16
http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:27092:27
_callTimer@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:30531:17
_callImmediatesPass@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:30570:17
callImmediates@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:30787:33
__callImmediates@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2736:35
http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2522:34
__guard@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2719:15
flushedQueue@http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false:2521:21
flushedQueue@[native code]
invokeCallbackAndReturnFlushedQueue@[native code]

Here is the Hello.sol:

pragma solidity 0.7.0;

contract Hello {
   address owner;
   uint256 value;

   event initContract(address _owner);
   constructor() {
      owner = msg.sender;
      emit initContract(owner);
   }

   function setValue(uint256 _value) public {
       value = _value;
  }

   function getValue() view public returns (uint256) {
      return value;
  }

}

I am following the doc for contract. The same functions are tested in truffle/ganache and they all work fine. What's missing here to call setValue (change state) and getValue (view only) functions in ethersjs?

Most helpful comment

Exactly what @zemse mentioned.

Also, in a real application, you won鈥檛 write a value to a contract and immediately read it. You would set up event listeners and let the events from you or other users trigger that.

You only need to await here because you are attempting consistent writes and reads.

A full application will have more complex interactions, but will be largely asynchronous. :)

All 12 comments

Is this using Ganache? Are you making sure the contract is deployed before calling these? Can you do a console.log(await provider.getCode(address));?

If you use a ContractFactory to deploy the contract, you can use await contract.deployTransaction.wait() to wait until the contract has finished deploying (the deploy transaction has been mined).

Let me know if that helps...

Yes, it is ganache 2.5.4. Here is the code of deployment which runs before the code of calling function:

//deploy
  const factory = new ContractFactory(abi, bytecode, signer);
  console.log("factory : ", factory);
  const contract = await factory.deploy();
  console.log("contract deployed : ", contract);
  console.log("contract address : ", contract.address);
  // The transaction that the signer sent to deploy
  contract.deployTransaction;
  // Wait until the transaction is mined
  contract.deployTransaction.wait();
  // Now the contract is safe to interact with

console.log(await provider.getCode(contract.address)); returns [Mon Nov 16 2020 19:35:04.373] LOG 0x

Yeah, your contract isn't deployed yet. You are missing an await here:

// Wait until the transaction is mined (note the await)
await contract.deployTransaction.wait();

Let me know if that helps. :)

Sure await works. I copied the code from an example online. Now the deploy went through. Here is the console.log(await provider.getCode(contract.address)):

[Mon Nov 16 2020 19:41:39.843]  LOG      0x6080604052348015600f57600080fd5b506004361060325760003560e01c80632096525514603757806355241077146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506088565b005b6000600154905090565b806001819055505056fea2646970667358221220e77caf623174d55e2deac837c30807b692b27511779573d093d655a323bfdbe564736f6c63430007000033

The getValue returns [Mon Nov 16 2020 19:43:23.167] LOG value contract at address : {"hex": "0x00", "type": "BigNumber"}. It shall return 10 instead.

Oh, you need to wait for the transaction to setValue be mined too:

// This is the transaction use to set the value; it has not been mined yet, just accepted by the network
const setTx = await contract.setValue(10);

// This actually waits for the transaction to complete, it has been mined after this await
const setReceipt = await setTx.wait();

// Now your get should work
console.log(await contract.getValue());

Give that a go... :)

bingo! The Fastest response I can ever image! Many thanks.

 [Mon Nov 16 2020 19:56:37.797]  LOG      value contract at address :  {"hex": "0x0a", "type": "BigNumber"}

Just suggestion. More example code in doc will help readers to have better understand of the API

Yeah... More doc examples is on my backlog. :)

I'm going to start running polls on Twitter to let people vote on where they'd like my time spent. :)

In real network, it would be take minutes to have the block mined and a user can't wait for such long. Shall the await (for mined) be removed from production release? Is it possible that a transaction was accepted by the network but never gets mined?

You are correct that for most users, the UX waiting for tx to be confirmed is annoying. You can have a look at Uniswap's UX how they handle this case. They push the transaction hash to a global array or transaction hashes. There user can click on a dropdown to see confirmation status and can do some more hacks with the transaction (like increasing gas price or so). This allows user to do other tasks in the dapp while tx is being confirmed in the background.

Exactly what @zemse mentioned.

Also, in a real application, you won鈥檛 write a value to a contract and immediately read it. You would set up event listeners and let the events from you or other users trigger that.

You only need to await here because you are attempting consistent writes and reads.

A full application will have more complex interactions, but will be largely asynchronous. :)

Some users may be satisfied by submitting the transaction and take mining for granted, as long as the submission will for sure triggers mining process. await for mining to complete may be used only as user requests.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thegostep picture thegostep  路  3Comments

moshebeeri picture moshebeeri  路  3Comments

adamdossa picture adamdossa  路  3Comments

dagogodboss picture dagogodboss  路  3Comments

jochenonline picture jochenonline  路  3Comments