15

In a Buidler test I have:

const { expect } = require("chai");

describe("SumOfTokens", function() {
  it("Checks correct transfers", async function() {
    const SumOfTokens = await ethers.getContractFactory("SumOfTokens");
    const sumOfTokens = await SumOfTokens.deploy();

    await sumOfTokens.deployed();

    console.log(await sumOfTokens.newToken());
  });
});

It prints some nonsense instead of the return value (that should be 1) of the external non-view function newToken.

How to obtain the return value of newToken after its call?

1
16

The return-value of a non-constant (neither pure nor view) function is available only when the function is called on-chain (i.e., from this contract or from another contract).

When you call such function from the off-chain (e.g., from an ethers.js script), you need to execute it within a transaction, and the return-value is the hash of that transaction.

This is because it is unknown when the transaction will be mined and added to the blockchain.

Moreover, even when the transaction is added to the blockchain, it can be removed from it later.

The longer it stays on the blockchain, the smaller the chances are that it will be removed from it later in the future.

It is custom to confirm 12 blocks before assuming that it will remain in the blockchain forever.

In order to obtain the return-value of a non-constant function when you call it from the off-chain, you can emit an event which contains the value which you are about to return.

The contents of this event will then be available to you within the transaction receipt, which you may obtain via the transaction hash.

3
  • 3
    Some questions: 1. All the troubles with return you told are also pertinent to events (events also need to be confirmed). So can I nevertheless obtain the return value (after enough confirmations)? How? 2. How to wait for 12 confirmations (not "12 blocks"... I think you have a typo?) and then to obtain the event? Please show the code. I am used to web3.js but not ethers.js
    – porton
    Oct 7 '20 at 4:41
  • "The return-value of a non-constant (neither pure nor view) function is available only when the function is called on-chain (i.e., from this contract or from another contract)." This is just plain incorrect. If there's a non-constant function you should be able to eth_call (off-chain) it to get the return value. This would obviously not modify storage permanently, but you should still be able to retrieve the return value(s).
    – Jonas Hals
    Sep 1 '21 at 8:06
  • This is helpful and educational, but the other answer mentioning callStatic is definitely worth mentioning.
    – Kyle Baker
    Oct 19 '21 at 16:38
8

While the return value of a function call executed on-chain cannot be returned off-chain, you can however simulate a function call on-chain to see what that function call would return.

In ethers, you can use callStatic. From ethers' documentation:

Rather than executing the state-change of a transaction, it is possible to ask a node to pretend that a call is not state-changing and return the result.

This does not actually change any state, but is free. This in some cases can be used to determine if a transaction will fail or succeed.

This otherwise functions the same as a Read-Only Method.

In your example, to simulate what sumOfTokens.newToken() would return if executed on-chain, you can do this:

console.log(await sumOfTokens.callStatic.newToken());
2
  • 1
    oh man, thanks fo much for this answer, this is exactly what I needed. The accepted answer makes it sound impossible.
    – Kyle Baker
    Oct 19 '21 at 16:37
  • This is the way to do it. Thank!
    – Steven K
    Dec 27 '21 at 23:26

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.