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.

up vote 8 down vote accepted

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

It is not currently possible to return values from functions which modify the blockchain. To receive a return value, you can mark read-only functions as "constant".

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

  • I wonder why I can't get a return value from my non-constant function? I have to use events? – Eamorr Apr 13 '17 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 – Taylor Gerring Apr 17 '17 at 18:36
  • So is there any sense in writing "returns (bool)" in "vote()" function? – A.K. Jun 1 '17 at 8:18
  • @Aniket yes, it makes sense if the function is called from another smart contract function – ledfusion Aug 14 at 18:24

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.

  • 1
    So is there any sense in writing "returns (bool)" in "vote()" function? – A.K. Jun 1 '17 at 8:18
  • 2
    @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 '17 at 1:30
  • got it completely – A.K. Jun 7 '17 at 5:35

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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