9

Solidity:

function ping() public view returns ( uint ) {
    return 999999999;
}
function ping2() public returns ( uint ) {
    return 999999999;
}

Javascript Ethers.js:

(await contract.ping()).toString();  //-> 999999999  ( correct ) 
(await contract.ping2()).toString(); //-> [object Object] ( ?? )

Why does ping2 return [Object Object] ? How to get the data from ping2?

Also:

(await contract.sendTransaction(txObj)).toString(); //-> [object Object] ( ?? )

Why does using sendTransaction also return [object Object] ?

Om Solari
  • 207
  • 1
  • 4
  • 13

2 Answers2

7

(await contract.ping()).toString(); //-> 999999999 ( correct )

ping() is a view function - you can just call it without creating a transaction. So ethers.js doesn't create a transaction and just returns result of the call.


(await contract.ping2()).toString(); //-> [object Object] ( ?? )

Why does using sendTransaction also return [object Object] ?

ping2() is a regular public function. Which suggests that you need to create a transaction to execute it (even though in this case it doesn't make any state changes so it could be a view function as well).

Ethers.js returns the transaction data instead of the contract function return value, when you're creating a transaction.

There are few ways to read values that the transaction produced using Ethers.js.

  • In this case, ping2() doesn't make any state changes and doesn't even read any blockchain data, so it could be a pure function. If it were reading blockchain data, it would be a view function... In both cases, ethers.js returns the result of the function call (not tx).

  • Transaction to a setter and calling a getter.

    contract MyContract {
        uint256 value;
    
        function setValue(uint256 _value) public {
            value = _value;
        }
    
        function getValue() public view returns (uint256) {
            return value;
        }
    }
    

    First you create a transaction that executes the setValue() function, and then you make a call to getValue() (without transaction, so it returns the value in your JS).

  • Reading event logs that your transaction produced

    event Transfer(address from, address to, uint256 amount);
    
    function transfer(address _to, uint256 _amount) public {
        emit Transfer(msg.sender, _to, _amount);
    }
    

    You can get the transaction receipt that also contains the event logs (in this case, the Transfer event and its values).

Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • 1
    Just trying to get data from calls to https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies – Om Solari Apr 24 '21 at 22:26
  • @OmSolari Using a proxy contract doesn't make any difference. You just need to distinguish between a call (no state changes) and a transaction (can make state changes), and the answer explains that it's not possible to get a return value from a **transaction** (it's possible to get it from a call), and also shows few workarounds. – Petr Hejda Apr 24 '21 at 22:51
  • Ok thanks. Is it possible to make a call to the fallback function of proxy contract? And if so whats the syntax using ethers.js? ( since fallback function doesn't exist in abi ) – Om Solari Apr 25 '21 at 10:48
  • I don't know the answer right away. But that's a separate question, so you should ask a new question here on StackOverflow, so that someone else will be able to answer that. – Petr Hejda Apr 25 '21 at 10:55
  • ok i posted it here: https://stackoverflow.com/questions/67259069/how-to-get-response-data-from-fallback-function-of-proxy-contract-using-ethers-j – Om Solari Apr 25 '21 at 23:11
7

Here is a great solution from Force Hero using Events.

In a nutshell:

const tx = await contract.transfer(...args); // 100ms
const rc = await tx.wait(); // 0ms, as tx is already confirmed
const event = rc.events.find(event => event.event === 'Transfer');
const [from, to, value] = event.args;
console.log(from, to, value);
Dharman
  • 30,962
  • 25
  • 85
  • 135
Carlitos
  • 343
  • 5
  • 7