I built an ERC1155 marketplace contract for creating, buying and selling NFTs.
The token creation works fine, but I’m having trouble with marketplace functionality. When someone tries to buy an NFT, the transaction fails and I can’t figure out why.
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’s 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) private _uris;
mapping(uint256 => ListingData) private listings;
Counters.Counter private _soldCounter;
struct ListingData {
uint256 id;
address payable creator;
address payable currentOwner;
uint256 cost;
bool purchased;
}
event ItemListed(
uint256 indexed id,
address creator,
address currentOwner,
uint256 cost,
bool purchased
);
using Counters for Counters.Counter;
Counters.Counter private _idCounter;
function updateBaseURI(string memory newUri) public onlyOwner {
_setURI(newUri);
}
function createToken(
string memory metadataUri,
uint256 supply,
uint256 cost
) public returns (uint256) {
uint256 currentId = _idCounter.current();
_mint(address(this), currentId, supply, "");
_setMetadata(currentId, metadataUri);
_idCounter.increment();
return currentId;
}
function listForSale(
uint256 id,
uint256 cost,
uint256 supply
) 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, supply, "");
emit ItemListed(
id,
msg.sender,
address(this),
cost,
false
);
}
function _setMetadata(uint256 id, string memory uri) private {
_uris[id] = uri;
}
function executePurchase(uint256 id, uint256 supply) 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));
_soldCounter.increment();
safeTransferFrom(address(this), msg.sender, id, supply, "");
setApprovalForAll(address(this), true);
payable(creator).transfer(msg.value);
}
function getAvailableItems() public view returns (ListingData[] memory) {
uint256 totalItems = _idCounter.current();
uint256 availableCount = _idCounter.current() - _soldCounter.current();
uint256 index = 0;
ListingData[] memory available = new ListingData[](availableCount);
for (uint256 i = 0; i < totalItems; i++) {
if (listings[i + 1].currentOwner == address(this)) {
uint256 currentId = i + 1;
ListingData storage item = listings[currentId];
available[index] = item;
index += 1;
}
}
return available;
}
}
I already tried using setApprovalForAll during minting but still get the same error. What am I missing here?