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;
}
}