[Ethereum] Calling Smart Contract function returns tx object instead of expected return value in Truffle test

truffleunittesting

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]);
    });

  });
});

Best Answer

.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.

Related Topic