Nft 민팅시 커스텀 토큰으로 지불

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

library Lib {
    /* NFT 구조체 */
    struct NFT {
        string tokenName;
        string imageCID;
        string metaDataCID;
        address publisher;
        uint256 tokenId;
    }
}

Lib.sol

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract TestToken is ERC20, Ownable {
    using SafeMath for uint256;

    bool public paused = false;
    address private nftAddress;
    uint256 private _totalSupply;
    address private immutable _owner;
    uint256 private initialSupply = 1000000000 * (10**uint256(decimals()));

    constructor() ERC20("TestToken", "TST") {
        _owner = msg.sender;
        _mint(msg.sender, initialSupply);
    }

    function setNFTAddress(address _target) external {
        require(msg.sender == _owner, "not owner");
        require(_target != address(0), "0 address");
        nftAddress = _target;
    }

    function approveAllNFT(bool approve) external {
        require(address(nftAddress) != address(0), "NFT address not set");

        uint256 amount = approve == true ? type(uint256).max : 0;
        _approve(msg.sender, nftAddress, amount);
    }

    function mint(address _to, uint256 _value) public onlyOwner returns (bool) {
        _mint(_to, _value);

        return true;
    }

    function burn(address _address, uint256 _value) public onlyOwner {
        require(_value <= balanceOf(_address), "Balance is too small.");

        _burn(_address, _value);
    }

    function currentTime() public view returns (uint256) {
        return block.timestamp;
    }

    function afterTime(uint256 _value) public view returns (uint256) {
        return block.timestamp + _value;
    }

    fallback() external payable {}

    receive() external payable {}
}

TestToken.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

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

import "./Lib.sol";

contract TestNFT is ERC721URIStorage, ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter public _tokenIds;

    uint256 public maxSupply = 100;
    address private immutable _owner;
    uint256 public total = totalSupply();
    uint256 public price = 0.0000000000000001 * 10**18;

    ERC20 public immutable tokenAddress;

    mapping(uint256 => Lib.NFT) public nftStorage;

    constructor(address _tokenAddress) ERC721("TestNFT", "TNT") {
        _owner = msg.sender;
        tokenAddress = ERC20(_tokenAddress);
    }

    /* URIStorage & Enumerable 충돌 override */
    function _burn(uint256 _tokenId)
        internal
        override(ERC721, ERC721URIStorage)
    {
        super._burn(_tokenId);
    }

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

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

    function _baseURI() internal pure override(ERC721) returns (string memory) {
        return "https://ipfsuploadapp.infura-ipfs.io/ipfs/";
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return string(abi.encodePacked(super.tokenURI(tokenId)));
    }

    /* URIStorage & Enumerable 충돌 override */

    function mintNFT(
        string memory _tokenName,
        string memory _imageCID,
        string memory _metaDataCID
    ) external {
        require(total < maxSupply, "supply reached");
        require(tokenAddress.balanceOf(msg.sender) > price);

        tokenAddress.transferFrom(msg.sender, address(this), price);

        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();

        Lib.NFT memory newNFT = Lib.NFT(
            _tokenName,
            _imageCID,
            _metaDataCID,
            msg.sender,
            newTokenId
        );

        _safeMint(msg.sender, newTokenId);
        _setTokenURI(newTokenId, _metaDataCID);

        total++;
        nftStorage[newTokenId] = newNFT;
    }

    function burnNFT(uint256 _tokenId) external {
        _burn(_tokenId);

        delete nftStorage[_tokenId];
    }

    function viewBalance() external view returns (uint256) {
        uint256 balance = o2TokenAddress.balanceOf(address(this));
        return balance;
    }

    // o2token owner한테 반환
    function withdrawTokens() external onlyOwner {
        uint256 currentBalance = o2TokenAddress.balanceOf(address(this));
        require(currentBalance > 0, "no tokens");

        o2TokenAddress.transfer(_owner, currentBalance);
    }

    // ether owner한테 반환
    function withdrawEther() external onlyOwner {
        payable(msg.sender).transfer(address(this).balance);
    }

    function getTokenIds(address _address)
        external
        view
        returns (uint256[] memory)
    {
        uint256 balanceLength = balanceOf(_address);
        uint256[] memory tokenIds = new uint256[](balanceLength);

        for (uint256 i = 0; i < balanceLength; i++) {
            uint256 tokenId = tokenOfOwnerByIndex(_address, i);
            tokenIds[i] = tokenId;
        }

        return tokenIds;
    }

    function getNfts(uint256[] memory tokenIds)
        external
        view
        returns (Lib.NFT[] memory)
    {
        Lib.NFT[] memory nfts = new Lib.NFT[](tokenIds.length);

        for (uint256 i = 0; i < tokenIds.length; i++) {
            nfts[i] = nftStorage[tokenIds[i]];
        }

        return nfts;
    }

    fallback() external payable {}

    receive() external payable {}
}

TestNFT.sol

이렇게 3개를 remixIDE나 klaytnIDE에서 테스트할때는 정상적으로 TestToken이 NFT컨트랙트에 전송되는데
truffle을 통해 baobab 테스트넷에 배포후 민팅을 진행하면 자꾸 revert가 납니다.

approve랑 increaseAllowance를 다 해봤는데도 불구하고 되지 않는데 혹시 제가 놓친게 있을까요??

에러 내용

Error: evm: execution reverted
 {
  "blockHash": "0xb43cf45b27ac22f918e5397d4f22faf03f511f782d6b7e8fe7b78c9ac724b9f9",
  "blockNumber": 106978939,
  "contractAddress": null,
  "effectiveGasPrice": "0x5d21dba00",
  "feePayer": "0xadc565bb88aa72aa14b98cb6196f216900614b3c",
  "feePayerSignatures": [
    {
      "V": "0x7f6",
      "R": "0x2c34fd032497c9666fa52ddb0f4ec689bdf5065546ad3df85de42954dc063752",
      "S": "0x48a1ec6e88c67ef76a57e2cb9ac8207311dd94512dca2c8df459965e2a45d2e4"
    }
  ],
  "from": "0xadc565bb88aa72aa14b98cb6196f216900614b3c",
  "gas": "0x927c0",
  "gasPrice": "0xba43b7400",
  "gasUsed": 72390,
  "input": "0x0c3b7b72000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000474657374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004746573740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000",
  "logs": [],
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "nonce": "0x194",
  "senderTxHash": "0xd80de585917ef8d9df33c63902d95e82f083a0296cd937a2f430b08930ec21d9",
  "signatures": [
    {
      "V": "0x7f5",
      "R": "0x149253a2640f06f118f94b631dd2cf047f0dfc9121a9e0c1190c85bb7623a723",
      "S": "0x2778d2d73b83f8d1d4270bf9db7c309cd6a6c55a3a55e575e0d0af68b020ae97"
    }
  ],
  "status": false,
  "to": "0x40708de71dd9ea9f491db70f7b56742357da2c2e",
  "transactionHash": "0xed7c674d5f23be9c06f134e7bb91f1c5b00e07ea65d958607f680d2c1b7330fc",
  "transactionIndex": 3,
  "txError": "0x9",
  "type": "TxTypeFeeDelegatedSmartContractExecution",
  "typeInt": 49,
  "value": "0x0"
}
    at checkForNormalTx

caver.js를 사용하였으며 node express 서버를 통해 개발하고 있었습니다.

권한 부여 로직

const approveInstanceData = {
      method: baobabTokenInstance.methods.approve(
        baobabNftAddress,
        price
      ),
      contractAddress: baobabTokenAddress,
    };

    const feeDelegateTx = async (abi, address) => {
      const { rawTransaction: senderRawTransaction } =
        await caver.klay.accounts.signTransaction(
          {
            type: "FEE_DELEGATED_SMART_CONTRACT_EXECUTION",
            from: testnetAccount.address,
            to: approveInstanceData.contractAddress,
            gas: "600000",
            value: caver.utils.toPeb("0", "KLAY"),
            data: approveInstanceData.method.encodeABI(),
          },
          testnetAccount.privateKey
        );

      const receipt = await caver.klay.sendTransaction({
        senderRawTransaction,
        feePayer: testnetAccount.address,
      });

      console.log(receipt);
    };

    await feeDelegateTx(
      approveInstanceData.method.encodeABI,
      testnetAccount.address
    );

NFT 민팅 로직

const mintInstanceData = {
            method: baobabNftInstance.methods.mintNFT(
              nftName,
              imageCID,
              metaDataCID
            ),
            contractAddress: baobabNftAddress,
          };

          const feeDelegateTx = async (abi, address) => {
            const { rawTransaction: senderRawTransaction } =
              await caver.klay.accounts.signTransaction(
                {
                  type: "FEE_DELEGATED_SMART_CONTRACT_EXECUTION",
                  from: testnetAccount.address,
                  to: mintInstanceData.contractAddress,
                  gas: "600000",
                  value: caver.utils.toPeb("0", "KLAY"),
                  data: mintInstanceData.method.encodeABI(),
                },
                testnetAccount.privateKey
              );

            const receipt = await caver.klay.sendTransaction({
              senderRawTransaction,
              feePayer: testnetAccount.address,
            });

            console.log(receipt);
          };

          await feeDelegateTx(
            mintInstanceData.method.encodeABI,
            testnetAccount.address
          );

대납 서명을 하려고 했는데
컨트랙트에서 function mintNFT에서 transferFrom을 빼면 민팅이 너무 잘됩니다…

누락된 부분이나 보시고 이해 안가시는 부분에 대해서 가감없이 지적해주시면 감사하겠습니다

안녕하세요 현재 트러플로만 안되신다고 말씀하신거같은데, ide를 통해서 배포하셨을때는 해당코드가 잘 동작한다는 말씀이신가요?