1

Counter.sol:

pragma solidity >=0.5.0 <0.7.0;

contract Counter {
    uint public _counter = 0;

    function GenNextID() public returns (uint) {
        return _counter++;
    }
}

I use Truffle and Ganache for local development.

My test looks like the following. For some reason ctrInstance.GenNextID.call() does NOT mutate _counter and it always remains equal to 0.

const Counter = artifacts.require("Counter");

contract("Counter", () => {
  it("should increment ocunter", async () => {
    const ctrInstance = await Counter.deployed();

    for (let i = 0; i < 10; i++) {
      const result = await ctrInstance.GenNextID.call();
      const id = result.toNumber();
      assert.equal(result, id); // this fails on second iteration
    }
  });
});

I figured out that call const r = await ctrInstance.GenNextID(); mutates internal state, but it always returns receipt but not returned data.

{
  tx: '0x9d0ef6530ba5808194612f7bf1b834b4ccab1c8e67aa58d44862d27dd777d57a',
  receipt: {
    transactionHash: '0x9d0ef6530ba5808194612f7bf1b834b4ccab1c8e67aa58d44862d27dd777d57a',
    transactionIndex: 0,
    blockHash: '0xe4883bcdfa9aecf0066228b1dc33ef61045f6a9a158fb0f331eb94afafaf08bf',
    blockNumber: 13,
    from: '0xe821f406192d16f9d7b74ece45e73ba92fb9ba64',
    to: '0x147e70c0babb11232d3df6f750fa9acd28a86460',
    gasUsed: 26700,
    cumulativeGasUsed: 26700,
    contractAddress: null,
    logs: [],
    status: true,
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    v: '0x1c',
    r: '0xdb56e87a6f52c0d03e7e3ddc1248d9cf55c38d0b747bebb4de4b8d21c94a28a',
    s: '0x41d2ba345d4f18be71aaceca1548a357f4ecdfbd6c51972bf6759e51035c67e5',
    rawLogs: []
  },
  logs: []
}

Question is: how to invoke GenNextID like a regular function, that is:

  1. call to this function mutates internal state
  2. it returns result returned from the contract's code
1

2 Answers 2

1

I see you modified the question after several valid/correct answers:

Question is: how to invoke GenNextID like a regular function, that is:

  • call to this function mutates internal state
  • it returns result returned from the contract's code

YOU DON'T

You either mutate the state and get a receipt, or you get a response but you don't mutate the state. At the risk of muddying the waters, the return value is available to other contract functions but is not available to external callers in any manner.

Please take the time to review the answers and internalize the information.


Have a look at this for some important clarification that will spare you some confusion about what's really going on. https://blog.b9lab.com/calls-vs-transactions-in-ethereum-smart-contracts-62d6b17d0bc2

This is a real-only (dry run) invokation because you used .call().

ctrInstance.GenNextID.call()

You need to use ctrInstance.GenNextId(); to make it mutate the state.

The next issue you will run into is you will receive only a transaction receipt and you need to wait for the transaction to be mined before the mutation will be detectable. With Ganache that is roughly instantaneous, but keep that tip in mind. It will bite later when there is a significant mining delay.

To see the mutation, you will have to inspect it, after it mines, with

myVar = await ctrInstance._counter();

In case it isn't clear:

  • The first async response is the mining receipt.
  • The second async response is inspecting the result.

Hope it helps.

0

A web3 call means you're just reading from a local node, not mutating the state of the blockchain, see What is the difference between a transaction and a call?

As you noticed, avoiding call and just calling the method on the web3 contract instance will mutate the contract state. This is because you are sending a transaction. In web3, sending a transaction will just return a promise containing a transaction receipt.

The reason it doesn't return the method's return value is because the blockchain is a distributed state machine and it literally does not know who to return the value to. The fact that you get a receipt and not just the transaction hash (the least info you can expect to get after submitting a transaction), is already some web3 magic.

Here are a couple ways of working around this:

  1. you can emit an event in the contract method and have web3 listen for the event.
  2. you can have another contract that calls the method and stores the result

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