2

I feel like I must be missing something trivial here. I have a smart contract that populates an array of structs shown below. When trying to call addNewQuestion('0x12345') or getQuestionListSize() it seems like it is never adding a new Question struct to questionList. When calling getQuestionListSize() it should be returning the counter which I named questionId but it is always just returning "1". Any help greatly appreciated.

Contract:

pragma solidity ^0.4.11;

contract EthUpVoting {

  struct Question {
    bytes32 ipfsHash;
    uint upvotes;
    address user;
  }

  uint private questionId;
  mapping (uint => Question) public questionList;

  event AddedQuestion(bytes32 ipfs_hash, uint qId);

  function EthUpVoting() public {
    questionId = 1;
    questionList[questionId] = Question({
        ipfsHash: "Hello, World!",
        upvotes: 0,
        user: msg.sender
    });
  }

  function addNewQuestion(bytes32 questionHash) public {
      questionId += 1;
      questionList[questionId] = Question({
          ipfsHash: questionHash,
          upvotes: 0,
          user: msg.sender
      });
      AddedQuestion(questionHash, questionId);
  }

  function voteForQuestion(uint qId) public {    
    questionList[qId].upvotes += 1;
    questionId += 1;
  }

  function getQuestionTextAtIndex(uint qId) public constant returns (bytes32 ipfsHashReturn) {
      return questionList[qId].ipfsHash;
  }

   function getQuestionListSize() public constant returns (uint size) {
      return questionId;
  }

}

NodeJS:

const web3 = new Web3(Web3.givenProvider || "ws://127.0.0.1:8546")
const ethUpVotingContract = new web3.eth.Contract(config.eth.abi, config.eth.contractAddr);

ethUpVotingContract.methods.addNewQuestion('foo').send(ethOptions, (err, resp) => {
    if (err) {
      console.log(err) 
    } else {
      // successfully returns transaction hash 
      console.log(resp);
    }
}).then(resp => {
    // successfully returns transaction receipt
    console.log(resp);
});

ethUpVotingContract.methods.getQuestionListSize().call().then(function (resp) {
    // this always returns "1"
    console.log(resp);
});
| improve this question | | | | |
  • why do you use ethUpVotingContract.methods. not just ethUpVotingContract – Etherkimist Oct 16 '17 at 13:23
  • I was just following the documentation: web3js.readthedocs.io/en/1.0/… – MannfromReno Oct 16 '17 at 15:11
  • In a previous version 'ws' provider was not working, I'd try 'http' and/or 'ipc' to check if they work, web3 v1.0 is still in beta so some things will not work and others have bugs. – Ismael Oct 16 '17 at 21:30
  • @Ismael so this methods thing is part of web3js 1.0 as new features? – Etherkimist Oct 16 '17 at 21:44
  • @Etherkimist Yes, web3 v1.0 is a refactorization from the old stable v0.20. I'd say learn web3 v1.0 because it is more future proof, but if you need something production ready I'd stick with v0.20 for the time being. Hopefully v1.0 will be released soon. – Ismael Oct 16 '17 at 21:52
2

I've tried your contract and found a couple of issues:

ethUpVotingContract.methods.addNewQuestion('foo')
.send(ethOptions, (err, resp) => {

Will fail with

throw new Error('Given parameter is not bytes: "'+ value + '"');

Error: Given parameter is not bytes: "foo" at SolidityTypeBytes.formatInputBytes [as _inputFormatter]

Converting to bytes will make it work

const question = web3.utils.asciiToHex('foo');

ethUpVotingContract.methods.addNewQuestion(question)
.send(options, (err, hash) => {

The other problem is that now send and call return a promise. What is happening? If the call promise start executing before the send has finished it will display the previous data as if the send have never succeeded.

To fix that you have to make the call inside the then clause of the send to ensure it has finished

const message = web3.utils.asciiToHex('foo');

ethUpVotingContract.methods.addNewQuestion(message)
.send(options, (err, hash) => {
  if (err) {
    console.log(err);
  }
  console.log(`TxHash: ${hash}`);
})
.then((result) => {
  console.log('Send has completed');
  // Here we are guarantee `send` has finished
  // and we can query the data
  ethUpVotingContract.methods.getQuestionListSize().call()
  .then((result) => {
    console.log('Call has returned');
    console.log(result);
  });
});

// If we call here perhaps `send` didn't finish yet
console.log('Send was called');
| improve this answer | | | | |
  • 1
    Thank you for taking the time to find the mistake and explain everything, very helpful. – MannfromReno Oct 18 '17 at 9:21

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.