Enable ERC20 payments for NFT minting contract

I’m having trouble with my smart contract setup and need some guidance.

I want to create an NFT contract that accepts both ETH and ERC20 tokens like USDC for payment. But I keep running into compilation issues with my imports.

The error I get is:

DeclarationError: Identifier already declared. --> contract-abc123.sol:10:1:
|
10 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

Here’s my current code:

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

import "@openzeppelin/contracts@4.8.0/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts@4.8.0/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts@4.8.0/access/Ownable.sol";
import "@openzeppelin/contracts@4.8.0/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract GameAsset is ERC721, ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;
    
    Counters.Counter private _idCounter;
    
    IERC20 public paymentToken;
    uint256 public nativePrice = 0;
    uint256 public tokenPrice = 0;
    uint256 public totalLimit;
    bool public saleActive;
    
    constructor(address _paymentToken) ERC721("GameNFT", "GNFT") {
        totalLimit = 500;
        paymentToken = IERC20(_paymentToken);
    }
    
    function updatePaymentToken(address _newToken) external onlyOwner {
        paymentToken = IERC20(_newToken);
    }
    
    function updatePrices(uint256 _nativePrice, uint256 _tokenPrice) external onlyOwner {
        nativePrice = _nativePrice;
        tokenPrice = _tokenPrice;
    }
    
    function purchaseWithToken(uint256 _amount) external {
        require(_amount >= tokenPrice, "Insufficient payment");
        paymentToken.transferFrom(msg.sender, owner(), _amount);
        createNFT();
    }
    
    function purchaseWithETH() external payable {
        require(msg.value >= nativePrice, "Insufficient payment");
        createNFT();
    }
    
    function createNFT() internal {
        require(saleActive, "Sale not active");
        require(totalSupply() < totalLimit, "Max supply reached");
        
        uint256 newId = _idCounter.current();
        _idCounter.increment();
        _safeMint(msg.sender, newId);
    }
    
    function toggleSale(bool _active) external onlyOwner {
        saleActive = _active;
    }
}

I’ve tried different import combinations but the error persists. What am I doing wrong with the ERC20 integration?

Hey! Cool setup. Quick question - are you mixing OpenZeppelin versions? I see @openzeppelin/contracts@4.8.0 for most imports but just @openzeppelin/contracts/token/ERC20/IERC20.sol without the version for ERC20. That might be causing conflicts.

Couple other things: what happens if someone sends more ETH than nativePrice? You’re not handling refunds. For ERC20 payments, do you want exact amounts or just >= tokenPrice?

Also curious why you made the payment token updatable. Planning to switch between different stablecoins? Interesting choice that could affect user experience.

What dev environment are you using? Remix, Hardhat, and Foundry sometimes handle imports differently.

Your compilation error comes from mixing versioned and unversioned OpenZeppelin imports. You’ve got @openzeppelin/contracts@4.8.0 in some places and @openzeppelin/contracts/token/ERC20/IERC20.sol in others. The compiler sees these as different paths and creates duplicate declarations. Fix your ERC20 import to match: import "@openzeppelin/contracts@4.8.0/token/ERC20/IERC20.sol"; But here’s a bigger problem - your ERC20 payment logic won’t work. The transferFrom call fails unless users approve your contract first. You need an approval check or make it a two-step process: approve, then purchase. Also add error handling for transferFrom since some tokens fail silently. Don’t assume all ERC20 tokens behave the same way - they don’t.