Enable USDT payments for NFT minting alongside ETH

Help with ERC20 token integration for NFT purchases

I’m working on a smart contract that should accept both ETH and USDT for minting NFTs. However I keep running into compilation issues when trying to import the ERC20 interface.

The error I get is:

DeclarationError: Identifier already declared

Here’s my contract 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 GameCard is ERC721, ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _cardIdCounter;
    
    IERC20 public paymentToken;
    uint256 public ethCost = 0;
    uint256 public tokenCost = 0;
    uint256 public totalCards;
    bool public salesActive;
    mapping(address => uint256) public userMints;

    constructor(address _paymentToken) ERC721("GameCard", "GCD") {
        totalCards = 50;
        paymentToken = IERC20(_paymentToken);
    }

    function updatePaymentToken(address _newToken) external onlyOwner {
        paymentToken = IERC20(_newToken);
    }

    function updatePricing(uint256 _ethCost, uint256 _tokenCost) external onlyOwner {
        ethCost = _ethCost;
        tokenCost = _tokenCost;
    }

    function purchaseWithToken() external {
        require(tokenCost > 0, "Invalid price");
        paymentToken.transferFrom(msg.sender, owner(), tokenCost);
        createCard();
    }

    function purchaseWithEth() external payable {
        require(msg.value >= ethCost, "Insufficient payment");
        createCard();
    }

    function createCard() internal {
        require(salesActive, "Sales paused");
        require(totalSupply() < totalCards, "No cards left");
        
        uint256 newCardId = _cardIdCounter.current();
        _cardIdCounter.increment();
        _safeMint(msg.sender, newCardId);
    }

    function toggleSales(bool _active) external onlyOwner {
        salesActive = _active;
    }

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

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 cardId,
        uint256 batchSize
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, cardId, batchSize);
    }
}

I’ve tried using both IERC20 and ERC20 imports but keep getting the same declaration error. What am I doing wrong with the token integration? Any help would be great since I’m still learning Solidity basics.

hey there! i think WhisperingTree nailed the main issue with the import versioning, but im curious about a few other things that might be causing headaches…

when you say “DeclarationError: Identifier already declared” - are you getting this error specifically on the IERC20 line or somewhere else? sometimes this error pops up if you’ve got duplicate variable names or function signatures conflicting between inherited contracts.

also noticed you’re setting both ethCost and tokenCost to 0 in the constructor - are you planning to update these before enabling sales? because right now someone could potentially mint for free if ethCost stays at 0 :thinking:

one more thing that caught my eye - you’re using owner() as the recipient in the transferFrom call, but depending on your setup, you might want to consider using a treasury address instead. just wondering what your plan is there?

have you tried compiling with just the basic ERC721 functionality first (without the USDT part) to see if the base contract works? might help isolate where exactly the declaration conflict is comming from.

btw what compiler version are you using in your hardhat/truffle config? sometimes there can be weird interactions between solidity versions and openzeppelin contract versions that cause these kinds of errors.

The compilation error you’re encountering is likely due to inconsistent import paths between OpenZeppelin versions. You’re mixing @4.8.0 versioned imports with an unversioned import for IERC20, which can cause conflicts.

I faced this exact issue when building my marketplace contract last year. The solution is to make all your OpenZeppelin imports consistent. Change your IERC20 import to:

import "@openzeppelin/contracts@4.8.0/token/ERC20/IERC20.sol";

Also, there’s a potential security issue in your purchaseWithToken function. You should check the return value of transferFrom or use SafeERC20 for better error handling. Some tokens don’t revert on failed transfers, which could allow free minting.

Your contract structure looks solid otherwise. The dual payment system is implemented correctly with separate functions for ETH and token payments.

looks like a version mismatch issue but also check if you have any other contracts or files importing IERC20 differently. i had similar problems and it turned out i was importing the same interface twice in different files. try cleaning your build artifacts first with npx hardhat clean then recompile - sometimes helps with wierd declaration errors.