Skip to main content
Glama
PrivateERC721.sol7.06 kB
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @title Private ERC721 Token for COTI Blockchain * @notice This is a base template for COTI private ERC721 (NFT) tokens * @dev Uses COTI's confidential types (ctString) for encrypted token URIs * * CUSTOMIZATION_MARKERS: * - MAX_SUPPLY: Add maximum NFT supply cap * - BURNABLE: Add burn functionality * - PAUSABLE: Add pause/unpause capability * - MINTABLE_RESTRICTIONS: Add minting restrictions (owner-only, time-based, etc.) * - ACCESS_CONTROL: Add role-based access control */ contract PrivateERC721 { string private _name; string private _symbol; // Token ownership and approvals mapping(uint256 => address) private _owners; mapping(address => uint256) private _balances; mapping(uint256 => address) private _tokenApprovals; mapping(address => mapping(address => bool)) private _operatorApprovals; // Note: In actual COTI contracts, tokenURIs use ctString (confidential string) // This is handled by COTI's compiler and runtime mapping(uint256 => string) private _tokenURIs; // In practice, this is ctString uint256 private _nextTokenId = 1; address public owner; // CUSTOMIZATION_MARKER:MAX_SUPPLY // uint256 public constant MAX_SUPPLY = 10000; event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); event Mint(address indexed to, uint256 indexed tokenId); // CUSTOMIZATION_MARKER:BURNABLE // event Burn(uint256 indexed tokenId); // CUSTOMIZATION_MARKER:PAUSABLE // bool public paused = false; // event Paused(address account); // event Unpaused(address account); modifier onlyOwner() { require(msg.sender == owner, "Only owner can call this function"); _; } // CUSTOMIZATION_MARKER:PAUSABLE // modifier whenNotPaused() { // require(!paused, "Contract is paused"); // _; // } constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; owner = msg.sender; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function balanceOf(address owner_) public view returns (uint256) { require(owner_ != address(0), "Balance query for zero address"); return _balances[owner_]; } function ownerOf(uint256 tokenId) public view returns (address) { address tokenOwner = _owners[tokenId]; require(tokenOwner != address(0), "Token does not exist"); return tokenOwner; } function tokenURI(uint256 tokenId) public view returns (string memory) { require(_owners[tokenId] != address(0), "Token does not exist"); return _tokenURIs[tokenId]; } function approve(address to, uint256 tokenId) public { // CUSTOMIZATION_MARKER:PAUSABLE // require(!paused, "Contract is paused"); address tokenOwner = ownerOf(tokenId); require(to != tokenOwner, "Approval to current owner"); require(msg.sender == tokenOwner || isApprovedForAll(tokenOwner, msg.sender), "Not owner nor approved for all"); _tokenApprovals[tokenId] = to; emit Approval(tokenOwner, to, tokenId); } function getApproved(uint256 tokenId) public view returns (address) { require(_owners[tokenId] != address(0), "Token does not exist"); return _tokenApprovals[tokenId]; } function setApprovalForAll(address operator, bool approved) public { // CUSTOMIZATION_MARKER:PAUSABLE // require(!paused, "Contract is paused"); require(operator != msg.sender, "Approve to caller"); _operatorApprovals[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function isApprovedForAll(address owner_, address operator) public view returns (bool) { return _operatorApprovals[owner_][operator]; } function transferFrom(address from, address to, uint256 tokenId) public { // CUSTOMIZATION_MARKER:PAUSABLE // require(!paused, "Contract is paused"); require(_isApprovedOrOwner(msg.sender, tokenId), "Not owner nor approved"); _transfer(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public { // CUSTOMIZATION_MARKER:PAUSABLE // require(!paused, "Contract is paused"); transferFrom(from, to, tokenId); } function mint(address to, string memory uri) public onlyOwner { // CUSTOMIZATION_MARKER:PAUSABLE // require(!paused, "Contract is paused"); // CUSTOMIZATION_MARKER:MAX_SUPPLY // require(_nextTokenId <= MAX_SUPPLY, "Minting would exceed max supply"); require(to != address(0), "Cannot mint to zero address"); uint256 tokenId = _nextTokenId; _nextTokenId++; _balances[to] += 1; _owners[tokenId] = to; _tokenURIs[tokenId] = uri; emit Mint(to, tokenId); emit Transfer(address(0), to, tokenId); } // CUSTOMIZATION_MARKER:BURNABLE // function burn(uint256 tokenId) public { // require(!paused, "Contract is paused"); // require(_isApprovedOrOwner(msg.sender, tokenId), "Not owner nor approved"); // // address owner_ = ownerOf(tokenId); // // _tokenApprovals[tokenId] = address(0); // _balances[owner_] -= 1; // delete _owners[tokenId]; // delete _tokenURIs[tokenId]; // // emit Burn(tokenId); // emit Transfer(owner_, address(0), tokenId); // } // CUSTOMIZATION_MARKER:PAUSABLE // function pause() public onlyOwner { // require(!paused, "Contract is already paused"); // paused = true; // emit Paused(msg.sender); // } // function unpause() public onlyOwner { // require(paused, "Contract is not paused"); // paused = false; // emit Unpaused(msg.sender); // } function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { address tokenOwner = _owners[tokenId]; require(tokenOwner != address(0), "Token does not exist"); return (spender == tokenOwner || getApproved(tokenId) == spender || isApprovedForAll(tokenOwner, spender)); } function _transfer(address from, address to, uint256 tokenId) internal { require(ownerOf(tokenId) == from, "Transfer from incorrect owner"); require(to != address(0), "Transfer to zero address"); _tokenApprovals[tokenId] = address(0); _balances[from] -= 1; _balances[to] += 1; _owners[tokenId] = to; emit Transfer(from, to, tokenId); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/davibauer/coti-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server