Hi
Here is my code:
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@limitbreak/creator-token-contracts/contracts/erc721c/ERC721C.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./SSTORE2.sol";
import "./Base64.sol";
contract SVGEditorNFTV3 is ERC721C, IERC2981, Ownable {
using Strings for uint256;
struct TokenData {
uint256 royalties;
address royaltyRecipient;
address attributesPointer;
}
mapping(uint256 => TokenData) private _tokenData;
uint96 private _platformRoyalty = 69;
uint256 private _payment = 96000000000000000;
bool public paused = false;
uint256 private _currentTokenId = 0;
constructor() ERC721OpenZeppelin("svg", "SVG") {}
mapping(address => bool) private _marketplaces;
// Modifier to check if the operator is either the owner or an approved marketplace
modifier onlyOwnerOrMarketplace(address owner, address operator) {
require(
owner == operator || _marketplaces[operator],
"Operator not approved"
);
_;
}
// Override isApprovedForAll to check for the modifier
function isApprovedForAll(address owner, address operator)
public
view
override(ERC721)
onlyOwnerOrMarketplace(owner, operator)
returns (bool)
{
return super.isApprovedForAll(owner, operator);
}
// Function to add a marketplace to the approved list
function addMarketplace(address marketplace) public onlyOwner {
_marketplaces[marketplace] = true;
}
// Function to remove a marketplace from the approved list
function removeMarketplace(address marketplace) public onlyOwner {
_marketplaces[marketplace] = false;
}
modifier whenNotPaused() {
require(!paused, "Contract is paused.");
_;
}
// Implement the _requireCallerIsContractOwner function
function _requireCallerIsContractOwner() internal view override {
require(owner() == _msgSender(), "Caller is not the contract owner");
}
function setPayment(uint256 x) external onlyOwner whenNotPaused {
_payment = x;
}
function returnPayment() external view returns (uint256) {
return _payment;
}
function setPlatformRoyalty(uint96 x) external onlyOwner whenNotPaused {
_platformRoyalty = x;
}
function platformRoyalty() external view returns (uint96) {
return _platformRoyalty;
}
function _setTokenRoyalty(uint256 tokenId, address recipient, uint256 royaltyPercentage) internal whenNotPaused {
require(royaltyPercentage <= 10000, "NFT: Royalty percentage must not exceed 10000");
_tokenData[tokenId].royalties = royaltyPercentage;
_tokenData[tokenId].royaltyRecipient = recipient;
}
function division(uint256 decimalPlaces, uint256 numerator, uint256 denominator) pure public returns(string memory result) {
uint256 factor = 10**decimalPlaces;
uint256 quotient = numerator / denominator;
bool rounding = 2 * ((numerator * factor) % denominator) >= denominator;
uint256 remainder = (numerator * factor / denominator) % factor;
if (rounding) {
remainder += 1;
}
result = string(abi.encodePacked(quotient.toString(), '.', numToFixedLengthStr(decimalPlaces, remainder)));
}
function numToFixedLengthStr(uint256 decimalPlaces, uint256 num) pure internal returns(string memory result) {
bytes memory byteString;
for (uint256 i = 0; i < decimalPlaces; i++) {
uint256 remainder = num % 10;
byteString = abi.encodePacked(remainder.toString(), byteString);
num = num/10;
}
result = string(byteString);
}
function mint(
string calldata svg,
string calldata name,
uint96 percent
) external whenNotPaused {
_currentTokenId++;
uint256 newTokenId = _currentTokenId;
_safeMint(msg.sender, newTokenId);
_setTokenRoyalty(newTokenId, 0xbe94F301cDb6eD1aFae186D79631B112129de041, _platformRoyalty);
// Convert percentage to string and append "%"
string memory percentString = division(2,percent,100);
//string memory percentWithSymbol = string(abi.encodePacked(percentString, "%"));
_tokenData[newTokenId].attributesPointer = SSTORE2.write(abi.encode(svg, name, percentString, msg.sender));
}
function tokenURI(uint256 tokenId) public whenNotPaused view virtual override returns (string memory) {
(string memory svg, string memory name, string memory percent, address minter) = abi.decode(SSTORE2.read(_tokenData[tokenId].attributesPointer), (string, string, string, address));
// Convert minter address to string
string memory minterString = _toString(minter);
string memory json = Base64.encode(
abi.encodePacked(
'{"name":"', name, '",',
'"image_data":"', svg, '",',
'"attributes":[{"trait_type":"Minter Fees (%)", "value": "', percent, '"},', // Notice that I've added double quotes around the value.
'{"trait_type":"Minter","value":"', minterString, '"}]',
'}'
)
);
return string(abi.encodePacked("data:application/json;base64,", json));
}
// Function to convert address to string
function _toString(address _addr) private pure returns(string memory) {
bytes32 value = bytes32(uint256(uint160(_addr)));
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(42);
str[0] = '0';
str[1] = 'x';
for (uint256 i = 0; i < 20; i++) {
str[2+i*2] = alphabet[uint8(value[i + 12] >> 4)];
str[3+i*2] = alphabet[uint8(value[i + 12] & 0x0f)];
}
return string(str);
}
function royaltyInfo(uint256 tokenId, uint256 value) external view virtual override returns (address receiver, uint256 royaltyAmount) {
receiver = _tokenData[tokenId].royaltyRecipient;
royaltyAmount = value * _tokenData[tokenId].royalties / 10000;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721C, IERC165) returns (bool) {
return super.supportsInterface(interfaceId) || interfaceId == type(IERC2981).interfaceId;
}
function pause() external onlyOwner {
paused = !paused;
emit Paused(paused);
}
event Paused(bool isPaused);
}
Every time I mint an NFT and sell it, users (not the owner) cannot seem to buy them. Every them they do they get errors saying "Operator not approved".
I am testing this on the goerli testnet and I do not know what the operator address for the goerli testnet is. I asked ChatGPT and they do not seem to give me an answer.
My intended output is that when someone makes a sale the royalties set stay fixed meaning not optional and not changeable and that all users can buy.
Is there anything that you can do to help me? Am I missing something?