30

I am writing below a simple contract which stores all results of questionnaires for each ID.

contract answer{
  mapping(address => mapping(string => bool)) voters;

  struct qList {
    uint count; //The number of respondents
    mapping(address => mapping(uint => uint)) answer;
  }

  mapping(string => qList) questionnaires;

  function vote(string ID, uint qNum, uint ans) returns (bool) {
    if(voters[msg.sender][ID]) throw;
    voters[msg.sender][ID] = true;
    questionnaires[ID].count += 1;
    questionnaires[ID].answer[msg.sender][qNum] = ans;
    return true;
  }

  function getNumResult(string ID) constant returns (uint res) {
    return questionnaires[ID].count;
  }
}

The function vote can be called and mined successfully, however I cannot get the return value of vote.

Appreciate it if someone would advise the cause of this and solution to get the return value of function with arguments.

4 Answers 4

11

Summary

Your code runs as expected when I execute it through the geth console. If it does not work for you, try increase the gas you send with your transactions.

As @Taylor Gerring has stated in his answer, you may not be able to get the results from your vote() function, but your code seems to work OK.

If you want the result from your vote() function, which in your example is a check as to whether a person has voted before, you already have this data in your voters data.



The Details

I've taken your source code and just changed the class name from answer to Answer, and converted your comment from // to /*...*/:

contract Answer {
  mapping(address => mapping(string => bool)) voters;

  struct qList {
    uint count; /* The number of respondents */
    mapping(address => mapping(uint => uint)) answer;
  }

  mapping(string => qList) questionnaires;

  function vote(string ID, uint qNum, uint ans) returns (bool) {
    if (voters[msg.sender][ID]) throw;
    voters[msg.sender][ID] = true;
    questionnaires[ID].count += 1;
    questionnaires[ID].answer[msg.sender][qNum] = ans;
    return true;
  }

  function getNumResult(string ID) constant returns (uint res) {
    return questionnaires[ID].count;
  }
}

I've stripped out the CR-LF from the code and collapsed the spaces, and executed the following statement in geth:

> var answerSource='contract Answer { mapping(address => mapping(string => bool)) voters; struct qList { uint count; /* The number of respondents */ mapping(address => mapping(uint => uint)) answer; } mapping(string => qList) questionnaires; function vote(string ID, uint qNum, uint ans) returns (bool) { if(voters[msg.sender][ID]) throw; voters[msg.sender][ID] = true; questionnaires[ID].count += 1; questionnaires[ID].answer[msg.sender][qNum] = ans; return true; } function getNumResult(string ID) constant returns (uint res) { return questionnaires[ID].count; }}'
undefined

I then compiled your code and inserted it into my dev blockchain:

> var answerCompiled = web3.eth.compile.solidity(answerSource);
undefined

> var answerContract = web3.eth.contract(answerCompiled.Answer.info.abiDefinition);
undefined 

> var answer = answerContract.new({
    from:web3.eth.accounts[0], 
    data: answerCompiled.Answer.code, gas: 2000000}, 
    function(e, contract) {
      if (!e) {
        if(!contract.address) {
          console.log("Contract transaction send: TransactionHash: " 
            + contract.transactionHash + " waiting to be mined...");
        } else {
          console.log("Contract mined! Address: " + contract.address);
          console.log(contract);
        }
    }
})

Contract transaction send: TransactionHash: 0x5b5eb3c6d2a4b43eff4444b71b762911ddc72e239d1d495b6bec7b2e6a738df0 waiting to be mined...

I waited for the contract to be mined and got the following message:

Contract mined! Address: 0xe51ac93e4c28206f0f0296e5b6d66daf0a917bc3
[object Object]

Checked getNumResult():

> answer.getNumResult("idOne")
0

Voted:

> answer.vote("idOne", 1, 1, eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});
"0xfcbf47472733c0922552aa617fc7cb1226c346edc022fbe09ea521dfb75f7699"

Waited for the transaction to be mined and checked the transaction:

> eth.getTransaction("0xfcbf47472733c0922552aa617fc7cb1226c346edc022fbe09ea521dfb75f7699")
{
  ...
  blockNumber: 6567,
  ...
}

Checked getNumResult():

> answer.getNumResult("idOne")
1

Sent another vote with a different ID:

> answer.vote("idTwo", 2, 1, eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});

Checked the results:

> answer.getNumResult("idTwo")
1

Sent another vote from the same account with the same ID:

> answer.vote("idOne", 2, 1, eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});
"0xbbf9db6eb7c02571948002f56e3a7c56b6c7f55c2a4bbc70a244bb2afbf44e1f"

And I noticed the following error:

PC 00000366: JUMP GAS: 976744 COST: 8 ERROR: invalid jump destination (PUSH1) 2

The error above must have been generated from the throw statement in your code if the same ID is voted from the same account:

if (voters[msg.sender][ID]) throw;

I then sent another vote from my second account:

> answer.vote("idOne", 2, 1, eth.accounts[1], {
  from:web3.eth.accounts[1], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});

And the results were updated as expected:

> answer.getNumResult("idOne")
2
1
  • Worked for me but I had to use require with condition negated by ! operator instead of throw. Sep 4, 2019 at 6:40
48

It is not currently possible to return values from functions which modify the blockchain. To receive a return value, you can mark functions as "pure" or "view".

For state-changing functions, the only way to "return" information is by using Solidity Events, which coalesce as LOG opcodes in the Ethereum Virtual Machine.

5
  • I wonder why I can't get a return value from my non-constant function? I have to use events?
    – Eamorr
    Apr 13, 2017 at 11:00
  • 1
    @Eamorr From a strictly technical perspective, it's not impossible but current client implementations don't lend themselves to such functionality Apr 17, 2017 at 18:36
  • So is there any sense in writing "returns (bool)" in "vote()" function?
    – Aniket
    Jun 1, 2017 at 8:18
  • @Aniket yes, it makes sense if the function is called from another smart contract function
    – brickpop
    Aug 14, 2018 at 18:24
  • 2
    How do we get this to be the accepted answer. The above one is misleading. Mar 18, 2021 at 18:56
7

With non-constant function vote, you can only get the transaction hash back immediately because the transaction may never get mined. Or it could take several blocks as indicated by "Waiting for transaction to be mined..." Recommend to check: What is the difference between a transaction and a call?

Events

Events are needed to get the "return value" of vote.

Example of how to add and trigger an event:

contract answer{
  // ...
  event VoteEvent(string ID, bool returnValue);

  function vote(string ID, uint qNum, uint ans) returns (bool) {
    // ...
    VoteEvent(ID, true);
    return true;
  }
}

See Contract Events for the different ways to watch for and get event data using web3.js.

3
  • 1
    So is there any sense in writing "returns (bool)" in "vote()" function?
    – Aniket
    Jun 1, 2017 at 8:18
  • 3
    @A.K. If vote might return false, for example if you've already voted, the return value could be helpful so that other contracts can do things like if (!someContract.vote("ak", 7, 2)) {...}. Return values can be used by other contracts, but web3.js can only use events. If it's always return true, then the bool can probably be removed and the event simplified to something like UserVoted(string ID).
    – eth
    Jun 7, 2017 at 1:30
  • got it completely
    – Aniket
    Jun 7, 2017 at 5:35
0

There is an untidy trick you can use during your development phase. When you have a function, myRealFunc(), whose return value you cannot access, have the myRealFunc() function write to a separate (and created just for this purpose) contract variable, myReturnValue, the value it is about to return. Then create a new function (again created just for this development phase) myDebugFunction() which does nothing other than return the value, my ReturnValue, stored by the previous myRealFunc() function call. Then from your truffle console or wherever you make the call to myDebugFunction and you get your previously hidden return value, myReturnValue. I find I have to make the call like this : instance.myDebugFunction.call() where instance has previously been setup as an instance of your smart contract.

This hack break several paradigms of good programming practice such as not modifying code in order to test it. You also have to remember to remove this hack code prior to deployment to a main net. That said, I have found it useful for my personal debug / development purposes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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