ERC1155 NFT Trading Platform Smart Contract Issue

I built an ERC1155 marketplace contract for creating, buying and selling NFTs.

The token creation works fine, but I have problems with the marketplace functions. When someone tries to buy an NFT, it throws an error even though I used setApprovalForAll during the minting process.

Market purchase test failed:
Error: VM Exception while processing transaction: reverted with reason string 'ERC1155: caller is not owner nor approved'
at TokenContract.balanceOf (@openzeppelin/contracts/token/ERC1155/ERC1155.sol:71)
at TokenContract.isApprovedForAll (@openzeppelin/contracts/token/ERC1155/ERC1155.sol:110)
at TokenContract.executePurchase (contracts/TokenContract.sol:165)

Here is my marketplace contract:

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

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract TokenMarketplace is ERC1155, Ownable, ERC1155Supply {
    constructor() ERC1155("") {}

    mapping(uint256 => string) internal _uris;
    mapping(uint256 => ListingData) private listings;
    Counters.Counter private _soldCount;

    struct ListingData {
        uint256 id;
        address payable creator;
        address payable currentOwner;
        uint256 cost;
        bool purchased;
    }

    event ListingCreated(
        uint256 indexed id,
        address creator,
        address currentOwner,
        uint256 cost,
        bool purchased
    );

    using Counters for Counters.Counter;
    Counters.Counter private _idCounter;

    function updateURI(string memory newUri) public onlyOwner {
        _setURI(newUri);
    }

    function createToken(
        string memory metadataUri,
        uint256 quantity,
        uint256 cost
    ) public returns (uint256) {
        uint256 currentId = _idCounter.current();
        _mint(address(this), currentId, quantity, "");
        _updateTokenUri(currentId, metadataUri);
        _idCounter.increment();
        return currentId;
    }

    function addToMarket(
        uint256 id,
        uint256 cost,
        uint256 quantity
    ) private {
        require(cost > 0, "Cost must be greater than zero");
        listings[id] = ListingData(
            id,
            payable(msg.sender),
            payable(address(this)),
            cost,
            false
        );
        setApprovalForAll(address(this), true);

        safeTransferFrom(msg.sender, address(this), id, quantity, "");

        emit ListingCreated(
            id,
            msg.sender,
            address(this),
            cost,
            false
        );
    }

    function _updateTokenUri(uint256 id, string memory metadataUri) private {
        _uris[id] = metadataUri;
    }

    function executePurchase(uint256 id, uint256 quantity) public payable {
        uint256 cost = listings[id].cost;
        address creator = listings[id].creator;

        listings[id].currentOwner = payable(msg.sender);
        listings[id].purchased = true;
        listings[id].creator = payable(address(0));

        _soldCount.increment();

        safeTransferFrom(address(this), msg.sender, id, quantity, "");
        setApprovalForAll(address(this), true);
        payable(creator).transfer(msg.value);
    }

    function getAvailableTokens() public view returns (ListingData[] memory) {
        uint256 totalCount = _idCounter.current();
        uint256 availableCount = _idCounter.current() - _soldCount.current();
        uint256 index = 0;

        ListingData[] memory tokens = new ListingData[](availableCount);
        for (uint256 i = 0; i < totalCount; i++) {
            if (listings[i + 1].currentOwner == address(this)) {
                uint256 currentId = i + 1;
                ListingData storage currentToken = listings[currentId];
                tokens[index] = currentToken;
                index += 1;
            }
        }
        return tokens;
    }
}

Your executePurchase function’s trying to transfer from the contract address, but the approval logic looks off. When you mint to address(this) in createToken, the contract automatically owns those tokens - no approval needed for self-transfers. But where’s addToMarket getting called? That might be what’s causing the ownership confusion.

Hmm, interesting issue! I’m curious about something - I see you’re calling setApprovalForAll(address(this), true) in both addToMarket and executePurchase, but where’s addToMarket being called from?

In your createToken function, you’re minting tokens directly to address(this) (the contract itself), but I don’t see where you actually call addToMarket to list them. Are you calling this separately after minting?

Also, when you say “I used setApprovalForAll during the minting process” - are you doing this from your frontend/test script as the original minter, or just using the approval calls inside the contract? If the contract’s trying to transfer tokens it minted to itself, it shouldn’t need approval since it already owns them.

Could you share your test setup? What’s the exact sequence of function calls? I’m thinking there’s a disconnect between who owns the tokens and who’s trying to transfer them during purchase.

Had the same issue building my ERC1155 marketplace last year. Your problem’s in the token flow - you’re minting to the contract address but never calling addToMarket to actually create the listing.

In createToken, tokens get minted to address(this), then nothing happens. addToMarket exists but it’s private and never gets called, so your listings mapping stays empty. When executePurchase runs, it’s working with uninitialized data.

Either make addToMarket public and call it after minting, or just build the listing logic into createToken. Also, you don’t need those setApprovalForAll calls in addToMarket - contracts can transfer tokens they own without approval. Same with the approval in executePurchase.

Just add the marketplace listing creation directly to createToken and that should fix your ownership issues.