1

I have write a simple smart contract ERC1155 allow ppl to mint NFTs and everything was ok except for the mint function. The transaction was ok but when i checked the balanceOf it always returned 0 even after minting

This is my token contract code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract TopSevenPlayer is Initializable, ERC1155Upgradeable, AccessControlUpgradeable, PausableUpgradeable, ERC1155BurnableUpgradeable, ERC1155SupplyUpgradeable, UUPSUpgradeable {
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize() initializer public {
        __ERC1155_init("https://staging-topseven.web.app/meta/{id}");
        __AccessControl_init();
        __Pausable_init();
        __ERC1155Burnable_init();
        __ERC1155Supply_init();
        __UUPSUpgradeable_init();

        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(PAUSER_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
        _grantRole(UPGRADER_ROLE, msg.sender);
    }

    function pause() public onlyRole(PAUSER_ROLE) {
        _pause();
    }

    function unpause() public onlyRole(PAUSER_ROLE) {
        _unpause();
    }

    function mint(address account, uint256 id, uint256 amount, bytes memory data)
        public
        onlyRole(MINTER_ROLE)
    {
        _mint(account, id, amount, data);
    }

    function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
        public
        onlyRole(MINTER_ROLE)
    {
        _mintBatch(to, ids, amounts, data);
    }

    function _beforeTokenTransfer(address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
        internal
        whenNotPaused
        override(ERC1155Upgradeable, ERC1155SupplyUpgradeable)
    {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
    }

    function _authorizeUpgrade(address newImplementation)
        internal
        onlyRole(UPGRADER_ROLE)
        override
    {}

    // The following functions are overrides required by Solidity.

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC1155Upgradeable, AccessControlUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

Minter contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import '@openzeppelin/contracts/access/Ownable.sol';

import './TopSevenPlayer.sol';

contract PublicMinter is Ownable {
  TopSevenPlayer private token;
  address private contractAddress;
  bool public IS_FREE_MINT = true;
  uint256 public BASE_PRICE = 500000000 gwei;

  event Log(uint256 amount, uint256 gas);
  event ResultsFromCall(bool success, bytes data);

  constructor() {}

  receive() external payable {}

  fallback() external payable {}

  /**
  ***************************
  Public
  ***************************
   */

  function mint(
    address to,
    uint256 tokenId
  ) public payable {
    if (!IS_FREE_MINT) {
      require(msg.value >= BASE_PRICE, "Need to send more MATIC");
    }
    token.mint(to, tokenId, 1, '');
    emit Log(msg.value, gasleft());
  }

  /**
  ***************************
  Customization for the contract
  ***************************
   */

  function setContractAddress(address payable _address) external onlyOwner {
    contractAddress = _address;
    token = TopSevenPlayer(_address);
  }

  function withdraw() external onlyOwner {
    uint256 balance = address(this).balance;
    require(balance > 0, 'No amount left to withdraw');

    (bool success, bytes memory data) = (msg.sender).call{value: balance}('');
    require(success, 'Withdrawal failed');
    emit ResultsFromCall(success, data);
  }

  function setIsFreeMint(bool _isFreeMint) public onlyOwner {
    IS_FREE_MINT = _isFreeMint;
  }

  function setBasePrice(uint256 _basePrice) public onlyOwner {
    BASE_PRICE = _basePrice;
  }
}

the test run ok as well:

token contract test

const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { expect } = require('chai');

const { ethers, upgrades } = require('hardhat');

describe('TopSevenPlayer', function () {
  // We define a fixture to reuse the same setup in every test.
  // We use loadFixture to run this setup once, snapshot that state,
  // and reset Hardhat Network to that snapshopt in every test.
  async function deployTopSevenPlayerFixture() {
    const [owner, otherAccount] = await ethers.getSigners();
    const TopSevenPlayer = await ethers.getContractFactory('TopSevenPlayer');
    const token = await upgrades.deployProxy(TopSevenPlayer);
    await token.deployed();
    return { token, owner, otherAccount };
  }

  describe('Deployment', function () {
    it('Should set the right owner', async function () {
      const { token, owner } = await loadFixture(deployTopSevenPlayerFixture);

      const isAdmin = await token.hasRole(
        await token.DEFAULT_ADMIN_ROLE(),
        owner.address
      );

      expect(isAdmin).to.equal(true);
    });

    it('Should get correct balance', async function () {
      const { token } = await loadFixture(deployTopSevenPlayerFixture);
      expect(await ethers.provider.getBalance(token.address)).to.equal(0);
    });
  });
});

minter contract test

const { assert } = require('chai');
require('chai').use(require('chai-as-promised')).should();

const { ethers, upgrades } = require('hardhat');

const BASE_PRICE = 1e18;

describe('PublicMinter', function () {
  let accounts, token, minter;
  beforeEach(async function () {
    accounts = await ethers.getSigners();
    const TopSevenPlayer = await ethers.getContractFactory('TopSevenPlayer');
    const PublicMinter = await ethers.getContractFactory('PublicMinter');

    token = await upgrades.deployProxy(TopSevenPlayer);
    minter = await PublicMinter.deploy();

    await minter.setContractAddress(token.address);
    await token.grantRole(await token.MINTER_ROLE(), minter.address);
  });

  describe('deployment', async () => {
    it('deploys successfully', async () => {
      const tokenAddress = token.address;
      assert.notEqual(tokenAddress, 0x0);
      assert.notEqual(tokenAddress, '');
      assert.notEqual(tokenAddress, null);
      assert.notEqual(tokenAddress, undefined);

      const minterAddress = minter.address;
      assert.notEqual(minterAddress, 0x0);
      assert.notEqual(minterAddress, '');
      assert.notEqual(minterAddress, null);
      assert.notEqual(minterAddress, undefined);
    });
  });

  describe('minting', async () => {
    it('free mint successfully', async () => {
      const account = accounts[0];

      await minter.mint(account.address, 1);

      const totalSupply = await token.totalSupply(1);
      assert.equal(totalSupply, 1);
      const balance = await token.balanceOf(account.address, 1);
      assert.equal(balance, 1);
    });

    it('free mint failed', async () => {
      const account = accounts[0];
      await minter.setIsFreeMint(false);
      minter
        .mint(account.address, 1)
        .should.be.rejectedWith('Need to send more MATIC');
    });

    it('mint successfully', async () => {
      const account = accounts[0];

      await minter.setIsFreeMint(false);

      const value = BASE_PRICE.toString();
      await minter.mint(account.address, 1, {
        from: account.address,
        value,
      });

      const totalSupply = await token.totalSupply(1);
      assert.equal(totalSupply, 1);
      const balance = await token.balanceOf(account.address, 1);
      assert.equal(balance, 1);
    });

    it('mint failed due to not enough matic', async () => {
      const account = accounts[0];

      await minter.setIsFreeMint(false);

      const value = (BASE_PRICE * 1e-3).toString(); // not enough
      minter
        .mint(account.address, 1, {
          from: account.address,
          value,
        })
        .should.be.rejectedWith('Need to send more MATIC');
    });
  });
});

This is my token contract on mumbai scan: https://mumbai.polygonscan.com/address/0xA64c09eAECC403b12D97852E3089a5F4670AB120#readProxyContract

and the minter contract, you can see i have mint transaction ok with tokenId = 1 https://mumbai.polygonscan.com/address/0x200643AC0DD4D043AA0Cd9036770066517C8aaFC

Im struggling with it in 2 days, its really weird when the tests run ok but the result after deploying is not ok

Every suggestion and explanation is appreciated, thank you guys

2
  • If I understood right, the problem only occurs once the contract has been deployed and you try to mint. Then my best guess is that you are not issuing a real transaction but only a static call to mint. Sep 25, 2022 at 17:30
  • Can you provide a failed transaction hash? You have to provide as much data as possible to reproduce the error, or review transactions that generated the error. Unfortunately token is a private variable and polygsonscan doesn't provide enough information to inspect the transaction trace.
    – Ismael
    Sep 28, 2022 at 3:54

1 Answer 1

0

If I understood correctly, I think you are using proxy contracts. You should use the address of proxy to do everything not the deployed contract address.

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.