Problems with minting when importing ERC20 Token into ERC721 contract

I can successfully compile and import all of my contracts. But upon importing my ERC20 token into my ERC721 contract my minting function does not work properly anymore. My first instinct is that the tokenID is causing the issue but after several days I still cannot really put my finger on it.

:1234: Code to reproduce

ERC721 contract

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "./SHELL.sol";

contract TurtleMint is ERC721Enumerable, Ownable {

  using Strings for uint256;

  string public baseURI;
  string public baseExtension = ".json";
  string public notRevealedUri;
  uint256 public cost = 0.05 ether;
  uint256 public maxSupply = 8888;
  uint256 public oneOfOneStartingIndex = 8868;
  uint256 public presaleSupply = 1000;
  uint256 public publicSupply = 4444;  
  uint256 public maxMintAmount = 5;
  uint256 public nftPerAddressLimit = 5;
  uint256 public totalMinted = 0; 
  uint256 public costShell = 1; 
  bool public paused = false;
  bool public revealed = false;
  bool public onlyWhitelisted = true;
  bool public burningPaused = true; 
  address[] public whitelistedAddresses;
  mapping(address => uint256) public addressMintedBalance;
  mapping(address => bool) private _whitelist;
  address payable public payments;
  SHELL public shell; 
  uint oneOfOneIndex = 8867;  //gas friendly bc no calculation when accessing array 
  uint[] public shellMintingCost = [1, 2, 3, 4, 4]; //last 2 are the same to avoid the unlikely case of pointing to NULL
  uint public currentShellMintingCost;
  uint[] public turtleBurningCost = [1, 2, 3, 4, 4]; 
  uint public currentTurtleBurningCost; 


  constructor(
    string memory _name,
    string memory _symbol,
    string memory _initBaseURI,
    string memory _initNotRevealedUri,
    address _payments,
    address _shell
  ) ERC721(_name, _symbol) {
    shell = SHELL(_shell);
    setBaseURI(_initBaseURI);
    setNotRevealedURI(_initNotRevealedUri);
    payments = payable(_payments);
  }

  // internal
  function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
  }

  // whitelisted minting
   /*bytes32 public merkleRoot = "";

    mapping(address => bool) public whitelistClaimed;

    function whiteListMint(bytes32[] calldata _merkleProof, uint amount) public {

        require(!whitelistClaimed[msg.sender], "Address has already claimed.");
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        require(MerkleProof.verify(_merkleProof, merkleRoot, leaf), "Invalid proof.");
        whitelistClaimed[msg.sender] = true;

        if(amount == 1) mint(1);
        else if(amount == 2) mint(2);
        else return;
    }
    */
  // public
  function mint(uint256 _mintAmount) public payable {
    uint256 supply = totalSupply(); 
    require(!paused, "the contract is paused");
    require(_mintAmount > 0, "need to mint at least 1 NFT");
    require(_mintAmount <= maxMintAmount, "max mint amount per session exceeded");
    require(supply + _mintAmount <= maxSupply, "max NFT limit exceeded");

    if (msg.sender != owner()) {
        if(onlyWhitelisted) {
            require(_whitelist[msg.sender], "Mint: Unauthorized Access");
            uint256 ownerMintedCount = addressMintedBalance[msg.sender];
            require(ownerMintedCount + _mintAmount <= nftPerAddressLimit, "max NFT per address exceeded");
            require(totalMinted + _mintAmount <= presaleSupply);  
        }
        require(totalMinted <= publicSupply, "Only mintable with $SHELL");
        require((totalMinted + _mintAmount) <= publicSupply, "Not enough public supply left, try a smaller amount");
        require(msg.value >= cost * _mintAmount, "insufficient funds");
    }
    for (uint256 i = 1; i <= _mintAmount; i++) {
      addressMintedBalance[msg.sender]++;
      _mint(msg.sender, totalMinted++); 
    }
  }
  
  /*function mintWithShell(uint256 _mintAmount) public payable {
    require(totalMinted >= publicSupply);
    require(totalMinted < oneOfOneStartingIndex, "Not enough Turtles left! :("); // not higher than 8868 so the 1of1s won't be given away
    costShell = currentShellMintingCost;
    require(msg.value >= costShell * _mintAmount, "insufficient $SHELL");
    shell.burn(_msgSender(), costShell * _mintAmount);
    for (uint256 i = 1; i <= _mintAmount; i++){
      if(totalMinted % 1111 == 0) {
          currentShellMintingCost = shellMintingCost[(totalMinted - 4444) / 1111];
       }
      addressMintedBalance[msg.sender]++;
      _safeMint(msg.sender, totalMinted++); // changed supply + i to totalMinted + i

    }
  }

  function burnTurtle(uint256 tokenID) external payable { // changed made payable
        require(burningPaused == false);
        require(msg.sender == ownerOf(tokenID));
        require(totalMinted < oneOfOneStartingIndex, "There are no more Turtles left to mint!");
        if(totalMinted % 1111 == 0) {
          currentTurtleBurningCost = turtleBurningCost[(totalMinted - 4444) / 1111];
       }
        require(msg.value >= currentTurtleBurningCost);
        _burn(tokenID);
        shell.burn(_msgSender(), currentTurtleBurningCost); // why not msg.sender?
        _safeMint(msg.sender, totalMinted++); // not sure; changed to totalMinted instead of supply
  }

  // maybe it would be smarter to just allow exactly 2 turtles to be burned --> see next function
  function burnMultipleTurtles(uint256 [] calldata tokenIDs) external {
    require(burningPaused == false);
    require(tokenIDs.length > 1, "You need to burn at least 2 Turtles!");
    for(uint256 i; i < tokenIDs.length; i++) {
      require(msg.sender == ownerOf(tokenIDs[i]));
      _burn(tokenIDs[i]); // this would actually burn one and if you dont own the second one that you want to burn you burned one for nothing, might wanna check in advance
      if(i % 2 == 1 ) {
        _safeMint(msg.sender, totalMinted++); // not sure; changed to totalMinted instead of supply, eigentlich wird das totalMinted ja eh erhöht also braucht man kein +i wenn ichs richtig verstehe
      }
    }
  }

  function burnTwoTurtles(uint256 turtle1, uint256 turtle2) external {
     require(burningPaused == false);
     require(msg.sender == ownerOf(turtle1) && msg.sender == ownerOf(turtle2), "Hey! You can only burn your own Turtles!");
     require(turtle1 != turtle2, "Hey! You need to use 2 different Turtles!");
     require(totalMinted < maxSupply, "There are no more Turtles left to mint!");
     _burn(turtle1);
     _burn(turtle2);
    if(random(turtle2) <= 5) { // keine ahnung welche zahlen das ausgibt, ist nur mal platzhalter
      if(oneOfOneIndex < maxSupply) { //oder kleiner gleich, je nachdem wie arrays zählen (array erstellen, letzes element ausgeben lassen, index anschauen)
        _safeMint(msg.sender, oneOfOneIndex++);
      }
      else { //wenns keine rares mehr gibt
        _safeMint(msg.sender, totalMinted++);
      }
    }
    else {
      _safeMint(msg.sender, totalMinted++); 
      
    }
  }
*/
  function walletOfOwner(address _owner) public view returns (uint256[] memory){
    uint256 ownerTokenCount = balanceOf(_owner);
    uint256[] memory tokenIds = new uint256[](ownerTokenCount);
    for (uint256 i; i < ownerTokenCount; i++) {
      tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
    }
    return tokenIds;
  }

  function tokenURI(uint256 tokenId) public view virtual override returns (string memory){
    require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
    if(revealed == false) {
        return notRevealedUri;
    }
    string memory currentBaseURI = _baseURI();
    return bytes(currentBaseURI).length > 0
        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
        : "";
}

 function transferFrom(
    address from,
    address to,
    uint256 tokenId
  ) public virtual override {
    //if (_msgSender() != address(staking))
      require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
    _transfer(from, to, tokenId);
  }

  function burn(uint256 tokenID) external  {
        require(burningPaused == false);
        require(msg.sender == ownerOf(tokenID));
        _burn(tokenID);
}

function burnMultiple(uint256 [] calldata tokenIDs) external {
require(burningPaused == false);
for(uint256 i; i < tokenIDs.length; i++) {
 require(msg.sender == ownerOf(tokenIDs[i]));
 _burn(tokenIDs[i]);
  if(i % 2 == 1 ) {
 //   _safeMint(msg.sender, supply + i); 
  }
 }
}

/*function checkWhitelist(address testAddress) external view returns (bool) {
    return bytes(currentBaseURI).length > 0 ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension)) : "";
  }*/ 

  function checkWhitelist(address testAddress) external view returns (bool) {
        if (_whitelist[testAddress] == true) { return true; }
        return false;
  }

  //only owner
  function reveal(bool _revealed) public onlyOwner {
      revealed = _revealed;
  }
  
  function setNftPerAddressLimit(uint256 _limit) public onlyOwner {
    nftPerAddressLimit = _limit;
  }
  
  function setCost(uint256 _newCost) public onlyOwner {
    cost = _newCost;
  }
  
  function setShellCost(uint256 _shellCost) external onlyOwner {
    costShell = _shellCost;
  }

  function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
    maxMintAmount = _newmaxMintAmount;
  }

  function setBaseURI(string memory _newBaseURI) public onlyOwner {
    baseURI = _newBaseURI;
  }

  function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
    baseExtension = _newBaseExtension;
  }
  
  function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
    notRevealedUri = _notRevealedURI;
  }

  function pause(bool _state) public onlyOwner {
    paused = _state;
  }
  
  function setOnlyWhitelisted(bool _state) public onlyOwner {
    onlyWhitelisted = _state;
  }
  
   function whiteListMany(address[] memory accounts) public onlyOwner {
        for (uint256 i; i < accounts.length; i++) {
            _whitelist[accounts[i]] = true;
        }
    }
  function setBurningPaused(bool _state)  public onlyOwner {
    burningPaused = _state; 
  }
  
   function withdraw() public payable onlyOwner {
    (bool success, ) = payable(owner()).call{value: address(this).balance}("");
    require(success);
  }
}

ERC20 Contract

// SPDX-License-Identifier: MIT LICENSE

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SHELL is ERC20, Ownable, ReentrancyGuard {

  // a mapping from an address to whether or not it can mint / burn
  mapping(address => bool) controllers;

  constructor() ERC20("SHELL", "SHELL") { }

  /*
   * mints $SHELL to a recipient
   * @param to the recipient of the $SHELL
   * @param amount the amount of $SHELL to mint
   */
  function mint(address to, uint256 amount) external nonReentrant {
    require(controllers[msg.sender], "Only controllers can mint");
    _mint(to, amount);
  }

  /*
   * burns $SHELL from a holder
   * @param from the holder of the $SHELL
   * @param amount the amount of $SHELL to burn
   */
  function burn(address from, uint256 amount) external nonReentrant {
    require(controllers[msg.sender], "Only controllers can burn");
    _burn(from, amount);
  }

  /*
   * enables an address to mint / burn
   * @param controller the address to enable
   */
  function addController(address controller) external onlyOwner {
    controllers[controller] = true;
  }

  /*
   * disables an address from minting / burning
   * @param controller the address to disbale
   */
  function removeController(address controller) external onlyOwner {
    controllers[controller] = false;
  }
}

:computer: Environment

Remix for testing

Are you combining ERC20 and ERC721 into the same contract? This is not something that you can do. They need to be separate.

If you want to combine fungible and non fungible tokens in the same contract you can look into ERC1155.