decodeFunctionResult: get output from a transaction receipt using ethers (js). maybe bug in ethers v5

Viewed 1609

The challenge is to extract an output uint256 from a receipt tx of a public smart contract using ethers in a js script that interacts with a smart contract.

Consider the following very simple contract1.sol. We are gonna play with the variable _val_1, and the function fun_sum256.

pragma solidity ^0.8.0;
// SPDX-License-Identifier: MIT
contract Contract1  {

  string public _str_1;
  uint256 public _val_1;

  constructor(string memory str_in1, uint256 in_val_1 ) {
    _str_1 = str_in1;
    _val_1 = in_val_1;
  } //endconstructor

  function get_str() external view returns (string memory) {
    return _str_1;
  } //endfun get_str

  function set_str(string memory str_in1) external returns (string memory) {
    _str_1 = str_in1;
    return _str_1;
  } //endfun set_str

  function fun_sum256(uint256 in_val_2) public returns (uint256) {
    _val_1 += in_val_2;
    return _val_1;
  } //endfun sum256

} //endcon

As observed in ethers, the procedure for decoding the tx_receipt.data requires the usage of an interface,

let value = contract.interface.decodeFunctionResult(fragment, result);

where fragment is the "fun_sum256" corresponding function fragment, and result, is the data of the tx-receipt (properly mined and waited). The function fragment can also be directly called in the following way, <yourDeployedContract>.interface.functions["fun_sum256(uint256)"].

It simply does not work, at least for me. I can extract the other string argument _str_1, but not the uint types. But on the contrary, on remix-ide, it does. If I init the _val_1 with value 1 in constructor, and then I invoke the fun_uint256 public function with an input of 2, it successfully works on remix, performing 1+2=3, to see the decoded output "0: uint256: 3". And indeed, from my js script that interacts with the contract I can foresee the value in the txdata, the last cypher: 0x78081f400000000000000000000000000000000000000000000000000000000000000003

But ethers v5 simply seems incapable of extracting that "3".

Can anyone provide a very simple example with a simple.sol and the corresponding simple.js to interact with, that works with ethers library in js?

1 Answers

Not possible by design.

A transaction return value is only available onchain, as a result of an internal transaction.

contract A {
    // mind the missing `view` keyword, suggesting a transaction
    function getBValue() external returns (uint256) {
        uint256 BValue = B(address(0x123)).getValue();
        // `BValue` is available onchain, but not offchain
        return BValue;
    }
}

contract B {
    // mind the missing `view` keyword, suggesting a transaction
    function getValue() external returns (uint256) {
        return 1;
    }
}

So when you execute A.getBValue(), the EVM also performs an internal transaction to B.getValue(). But the value is not available outside of this scope (e.g. in the transaction receipt).


On the other hand, a call return value is availabe both onchain and offchain.

contract A {
    // mind the existing `view` keyword, suggesting a call
    function getBValue() external view returns (uint256) {
        uint256 BValue = B(address(0x123)).getValue();
        // `BValue` is available both onchain and offchain
        return BValue;
    }
}

contract B {
    // mind the existing `view` keyword, suggesting a call
    function getValue() external view returns (uint256) {
        return 1;
    }
}

When you make a call to A.getBValue(), you are able to get the value offchain.


Suggestion: In this case, you can mark the fun_sum256() function as view (docs), which will make the ethers library to perform a call instead of a transaction, so that the returned value will be available in your JS code.

Related