[SOLVED] Deployment: return value with NON view/pure function


So I’m writing this simple ERC1155. I’m also using Couters.sol:

import "@openzeppelin/contracts/utils/Counters.sol";

My very basic ERC1155 looks like this:

contract Token is ERC1155 {

  using Counters for Counters.Counter;
  Counters.Counter private _tokenIds;

  constructor(string memory uri_) ERC1155(uri_){}

  function mintItem(uint256 amount_) public returns (uint256) {

    uint256 newItemId = _tokenIds.current();
    _mint(msg.sender, newItemId, amount_, "");

    return newItemId;

  function currentId() public view returns (uint256) {
    return _tokenIds.current();

Now when I’m minting using mintItem() function I’m getting as a response a TX, which when finished mining looks like:

  hash: '0xbb05d8640b16500eb280a7b1fba76c9d8773c22e9222626d66848b164ecf93e5',
  type: 2,
  accessList: [],
  blockHash: '0x29192d29fc17612aa975574018d71f0894de91444a08348bb971aa849740ea58',
  blockNumber: 7,
  gasPrice: BigNumber { _hex: '0x5399d4a7', _isBigNumber: true },
  maxPriorityFeePerGas: BigNumber { _hex: '0x3b9aca00', _isBigNumber: true },
  maxFeePerGas: BigNumber { _hex: '0x6b98df4e', _isBigNumber: true },
  gasLimit: BigNumber { _hex: '0x01bad518', _isBigNumber: true },
  to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  value: BigNumber { _hex: '0x00', _isBigNumber: true },
  nonce: 6,
  data: '0x17fb85940000000000000000000000000000000000000000000000000000000000000096',
  r: '0x3eae88d7172f1e7adaa68dcc768c975ce91f95870cd4b08ded8377ecfbfd2fa3',
  s: '0x68691235f8a355fe8e26fbbc307022a49378af399f614eb252f46d79618f56c1',
  v: 0,
  creates: null,
  chainId: 31337,
  wait: [Function (anonymous)]

Now here’s a problem, the mintItem() function should return the Counters.Counter which is a struct with uint256. When I run the currentId() I get the proper id. But how can I get the same thing from mintItem() - to return the value of the id. Because that’s what it returns actually states in the contract, that it returns the _tokenIds. But because it’s not pure/view it returns the TX.

At first I though it’s the data string, but actually it’s not. The id number doesn’t match.

I’m using Hardhat and ethers.js

Btw I’m talking about the deployment and testing part. So this is what I managed to figure out:

    Token = await ethers.getContractFactory("Token");
    token = await token.deploy("https://ipfs.io/ipfs/{cid}/");
    await token.deployed();
    const mintingTx = await token.mintItem(150);

Now this make sense that the actual minting TX returns what I want:

    const counter = await mintingTx.wait();

But this seams to be overengineered:

    tokenId = counter.events[0].data;
    console.log("ID: ", parseInt(Number(tokenId)));

But yeah, it returns what I want. But like I said, isn’t this a bit overengineered?

I had the impression that you need to use an event to get that data in the transaction output

So to make it clear. Is the event[0] the actual emited event from the _mint() function of the ERC1155? This how it looks like:

    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);

So is this emit TransferSingle(operator, address(0), to, id, amount) the event I’m using here counter.events[0].data ?

first you will have to see the events for that transaction, and seeing those events may also get you to that return value in your case, then for every event should be a topic0 that matches the hash for the event signature in order to identity what event it is

I was thinking in emiting a different event specific to that ID, but you could get the id from that TransferSingle event too if it is the same id

1 Like

Yeah. And Tbh I don’t think it’s safe to use that .data, as I need rounding do get the integer.

So this is what the event from the mined transaction looks like. I removed things that we might not need:

  // removed stuff here
  data: '0x00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000096',
  // some removed stuff here
  args: [
    BigNumber { _hex: '0x06', _isBigNumber: true },
    BigNumber { _hex: '0x96', _isBigNumber: true },
    operator: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    from: '0x0000000000000000000000000000000000000000',
    to: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
    id: BigNumber { _hex: '0x06', _isBigNumber: true },
    value: BigNumber { _hex: '0x96', _isBigNumber: true }
  // remove one line
  event: 'TransferSingle',
  eventSignature: 'TransferSingle(address,address,address,uint256,uint256)',
  // and remove more here

So since I have the signature TransferSingle(address,address,address,uint256,uint256) and I know the 4th argument I pass is the new ID. Then the proper way to return the ID is:

    const tokenIdInt = parseInt(minted.events[0].args[3]);

Thank you! Now it all makes sense!

1 Like

One last thing, so that I remember this lesson forever. If you look closer at args you can find the actual props of the event. So I think this is how you should do it:

    const mintingTx = await token.mintItem(150);
    const minted = await mintingTx.wait();
    const eventTransferSingle = minted.events[0];
    // 0 is just because I have just one event emitted
    const tokenIdInt = parseInt(eventTransferSingle.args.id);
1 Like