1

I'm trying to create a basic ERC721 NFT, using OpenZeppelin contarct. I started with something similar to their example code:

//SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721Burnable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Artist is ERC721, ERC721Burnable {
  using Counters for Counters.Counter;
  Counters.Counter private _tokenIds;

  constructor(string memory name, string memory symbol, string memory baseURI) public ERC721(name, symbol) {
    _setBaseURI(baseURI);
  }

  function createArtist(address artist, string memory profileURI) public returns (uint256) {
    _tokenIds.increment();
    uint256 newItemId = _tokenIds.current();
    _safeMint(artist, newItemId);
    _setTokenURI(newItemId, profileURI);

    return newItemId;
  }
}

My problem is, when I call createArtist in my test like this:

it('mints properly', async () => {
  instance.Transfer().on('data', data => console.log('data', data));
  const id = await instance.createArtist(accounts[0], 'myurl');
  console.log(id);
  assert(id > 0);
});

instead of getting an ID back, I get this:

{
  tx: '0xfca7bcbc2e860d1a7f63df808ae0b974a80801f3ebb745b213ac9622374492c3',
  receipt: {
    transactionHash: '0xfca7bcbc2e860d1a7f63df808ae0b974a80801f3ebb745b213ac9622374492c3',
    transactionIndex: 0,
    blockHash: '0x5532b7dbdb888e878890f73e1e346ba8ab71602dfa43f621a3ac9a9efc4e6f49',
    blockNumber: 39,
    from: '0x944274a88ac56b20fb1d1ec01d2c9410606d851f',
    to: '0x34d3941670f5580a97010aecb73110394ddc7fb2',
    gasUsed: 216872,
    cumulativeGasUsed: 216872,
    contractAddress: null,
    logs: [ [Object] ],
    status: true,
    logsBloom: '0x00000000000000000000000000000000000000000000000000000004000000000001000000000000000000080000000000000000000000000000000000040002000000000000000000002008000000000000000000040000000000000000000000000000020000000000000000000800000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000040000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000',
    rawLogs: [ [Object] ]
  },
  logs: [
    {
      logIndex: 0,
      transactionIndex: 0,
      transactionHash: '0xfca7bcbc2e860d1a7f63df808ae0b974a80801f3ebb745b213ac9622374492c3',
      blockHash: '0x5532b7dbdb888e878890f73e1e346ba8ab71602dfa43f621a3ac9a9efc4e6f49',
      blockNumber: 39,
      address: '0x34d3941670F5580a97010AEcB73110394dDc7fb2',
      type: 'mined',
      id: 'log_93167e1c',
      event: 'Transfer',
      args: [Result]
    }
  ]
}     

I know _mint emits a Transfer event, but the event code I've added doesn't get triggered, and I still cannot get the ID back.

I'm not using Truffle, not ethers.js. I'm trying to handle the event/response in tests.

What am I doing wrong?

5
  • @goodvibration thanks. It definitely explains the root cause, but I did not see any solution. I need a way to ultimately show the user the ID of his token, and also a way to test. If the value neither returns, nor gets emitted in an event - how can I proceed? Oct 22 '20 at 18:25
  • I'm trying to create a basic ERC721 NFT - add an event before (or instead of) returning the value. Pu that returned-value in the event, then it will be available to you within the transaction receipt. This is also explained in the answer which I have linked above. Oct 22 '20 at 18:33
  • Tried your suggestion. Returned value in event instead of a return value. My test still cannot handle that event. Are you suggesting I should parse the receipt returned from the function, and look for the event parameter? Oct 22 '20 at 18:42
  • That's one option. Other options are to listen to the event, or to search for it in the blockchain after the transaction completes. It really depends on what infrastructure exactly you're using (e.g., Truffle, web3.js, web3.py, ethers.js), and even on which version of that infrastructure, as it's kinda "work in progress". But the general idea, also explained in the answer that I linked above, is that when you execute a non-constant function within a transaction, the value returned to the offchain is the details of the transaction rather than the returned-value of the function. Oct 22 '20 at 19:00
  • I get the point of not bothering to return the value from the function. And OZ implementation of ERC721 already includes the value I need in the Transfer event they emit. So how would you write this test with Truffle? That is the essence of my question. Oct 23 '20 at 0:16
2

You can call a Truffle non-view function as view function using contractInstance.methodName.call:

it('mints properly', async () => {
  instance.Transfer().on('data', data => console.log('data', data));
  const id = await instance.createArtist.call(accounts[0], 'myurl');
  console.log(id);
  assert(id > 0);
});
1
  • A call doesn't create a transaction so no events will be raised. Any modification will be temporary and discarded immediately at the end. Outside a test is not safe to use this mechanism to obtain a returned value.
    – Ismael
    Oct 23 '20 at 15:59

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