1

I have started writing a smart contract for a gift registry.

When I write tests using the Truffle Framework I get unexpected return values.

For example, when calling the offerGift function from within the test it returns a tx object instead of the id.

If I use offerGift.call(...) it returns the id but when I call getGift the gift can't be found, and I get the error

Error: Returned error: VM Exception while processing transaction: revert Gift not found

I have tried the answers provided in Returns TX Object instead result in Truffle test but they didn't work for me.

Using
Truffle v5.0.18 (core: 5.0.18)
Solidity v0.5.0 (solc-js)
Node v10.15.3
Web3.js v1.0.0-beta.37
Ganache v1.3.1
truffle-assertions 0.9.0

What am I doing wrong?

The Contract Code

pragma solidity >=0.4.21 <0.6.0;

contract GiftRegistry {

  uint256 idCounter = 0;
  enum GiftStatus { Offered }

  struct Gift {
    string description;
    uint256 value;
    GiftStatus status;
    address giftGiver;
    address giftReceiver;
    address giftApprover;
  }

  mapping(uint256 => Gift) public giftMap;

  function doesGiftExist(uint256 _giftId) private view returns(bool){
    Gift memory gift = giftMap[_giftId];
    bytes memory giftAsBytes = bytes(gift.description);

    return giftAsBytes.length > 0;
  }

  function getGift(uint256 _giftId) public view returns(string memory, uint256, GiftStatus, address, address, address){
    require(doesGiftExist(_giftId), 'Gift not found');
    Gift memory gift = giftMap[_giftId];

    return (
      gift.description,
      gift.value,
      gift.status,
      gift.giftGiver,
      gift.giftReceiver,
      gift.giftApprover
    );
  }

  function offerGift(string memory _description, uint256 _value, address _giftReceiver) public returns(uint256) {
    uint256 currentId = idCounter;
    giftMap[currentId] = Gift(_description, _value, GiftStatus.Offered, msg.sender, _giftReceiver, address(0));

    idCounter = idCounter + 1;

    return currentId;
  }
}

The Truffle Test

const GiftRegistry = artifacts.require('GiftRegistry');
const truffleAssert = require('truffle-assertions');

contract('The Gift Registry contract', accounts => {
  let contract;
  const giftGiverAddress = accounts[1];
  const giftReceiverAddress = accounts[2];

  beforeEach(async () => {
    contract = await GiftRegistry.new({from: accounts[0]});
  });

  describe('given offerGift is called', () => {
    const OfferedStatus = 0;

    it('the status should be set to offered', async () => {
      const fiveDollars = 500;
      // Calling like this returns a tx instead of the id, the gift can be found with a hard coded index of 0
      const giftId1 = await contract.offerGift('coffee', fiveDollars, giftReceiverAddress, {from: giftGiverAddress});
      // This call returns the new giftId, however the gift can't be found and the call to getGift throws the error
      // Error: Returned error: VM Exception while processing transaction: revert Gift not found
       const giftId2 = await contract.offerGift.call('coffee', fiveDollars, giftReceiverAddress, {from: giftGiverAddress});

      const gift = await contract.getGift.call(0);

      assert.equal(OfferedStatus, gift[2]);
    });

  });
});
3
  • 1
    A non-constant (either pure or view) function returns the hash of the transaction when executed from the off-chain (i.e., by an externally-owned account). Such function should return something only if meant to be executed from the on-chain (i.e., by this contract or by some other contract). When called from the off-chain, they will always return the hash of the transaction (even if they are not declared to return anything). So unless you intend to call it from the on-chain, there is no point for this function to return anything. Commented May 23, 2019 at 13:46
  • @goodvibration removing the view from getGift gives me the same result. Commented May 23, 2019 at 13:51
  • What does changing getGift has anything to do with your question, and what do you mean by "same result"? Same result as what? Commented May 23, 2019 at 13:52

1 Answer 1

1

.call() pretends to do the transaction and gives you the result but doesn't commit any changes back to the blockchain.

You can find more details at Truffle docs but it will say basicly that

When you execute a contract function via a call you will receive the return value immediately

Executing getGift after offerGift.call(...) will not work because idCounter wasn't updated.

You have to contract.offerGift('coffee', fiveDollars, giftReceiverAddress, {from: giftGiverAddress}); and you will get the tx object. Then you have to check if it was mined and then get the log and from the log event, get the gift id that you were expecting. You won't get the return value.

2
  • That seems like more code than I was hoping to write for a simple test :( Commented May 23, 2019 at 13:54
  • Well, depends of what you would like to test. In this case, if I were you, I would just make idCounter public (or create a view function to get its value) and then check its value and comparing to what you expect. Something like this: contract.idCounter({from: giftGiverAddress}) Commented May 23, 2019 at 19:32

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.