Transaction Hash:
Block:
14208982 at Feb-15-2022 05:56:39 AM +UTC
Transaction Fee:
0.001969544866127358 ETH
$4.44
Gas Used:
46,241 Gas / 42.593042238 Gwei
Emitted Events:
37 |
NtentArt.ApprovalForAll( owner=[Sender] 0x93752c39b079f622c6d31256951e2c8044f57ef2, operator=0x38Dc6542...CC4C74A1d, approved=True )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x829BD824...93333A830
Miner
| (F2Pool Old) | 1,454.19786500896526661 Eth | 1,454.19793437046526661 Eth | 0.0000693615 | |
0x8Ec319e4...0C613f01d | |||||
0x93752c39...044f57Ef2 |
0.01411527902539169 Eth
Nonce: 349
|
0.012145734159264332 Eth
Nonce: 350
| 0.001969544866127358 |
Execution Trace
NtentArt.setApprovalForAll( to=0x38Dc6542BAe3E77D64B67176dF5a589CC4C74A1d, approved=True )
setApprovalForAll[IERC721 (ln:137)]
// SPDX-License-Identifier: MIT // File contracts/libs/IERC165.sol // File: openzeppelin-solidity/contracts/introspection/IERC165.sol pragma solidity ^0.8.9; /** * @dev Interface of the ERC165 standard, as defined in the * [EIP](https://eips.ethereum.org/EIPS/eip-165). * * Implementers can declare support of contract interfaces, which can then be * queried by others (`ERC165Checker`). * * For an implementation, see `ERC165`. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified) * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); } // File contracts/libs/ERC165.sol // File: openzeppelin-solidity/contracts/introspection/ERC165.sol pragma solidity ^0.8.9; /** * @dev Implementation of the `IERC165` interface. * * Contracts may inherit from this and call `_registerInterface` to declare * their support of an interface. */ contract ERC165 is IERC165 { /* * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 */ bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; /** * @dev Mapping of interface ids to whether or not it's supported. */ mapping(bytes4 => bool) private _supportedInterfaces; constructor () { // Derived contracts need only register support for their own interfaces, // we register support for ERC165 itself here _registerInterface(_INTERFACE_ID_ERC165); } /** * @dev See `IERC165.supportsInterface`. * * Time complexity O(1), guaranteed to always use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) override external view returns (bool) { return _supportedInterfaces[interfaceId]; } /** * @dev Registers the contract as an implementer of the interface defined by * `interfaceId`. Support of the actual ERC165 interface is automatic and * registering its interface id is not required. * * See `IERC165.supportsInterface`. * * Requirements: * * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). */ function _registerInterface(bytes4 interfaceId) internal { require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); _supportedInterfaces[interfaceId] = true; } } // File contracts/libs/IERC721.sol // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol pragma solidity ^0.8.9; /** * @dev Required interface of an ERC721 compliant contract. */ abstract contract IERC721 is IERC165 { 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); /** * @dev Returns the number of NFTs in `owner`'s account. */ function balanceOf(address owner) public virtual view returns (uint256 balance); /** * @dev Returns the owner of the NFT specified by `tokenId`. */ function ownerOf(uint256 tokenId) public virtual view returns (address owner); /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * * * Requirements: * - `from`, `to` cannot be zero. * - `tokenId` must be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this * NFT by either `approve` or `setApproveForAll`. */ function safeTransferFrom(address from, address to, uint256 tokenId) public virtual; /** * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to * another (`to`). * * Requirements: * - If the caller is not `from`, it must be approved to move this NFT by * either `approve` or `setApproveForAll`. */ function transferFrom(address from, address to, uint256 tokenId) public virtual; function approve(address to, uint256 tokenId) public virtual; function getApproved(uint256 tokenId) public virtual view returns (address operator); function setApprovalForAll(address operator, bool _approved) public virtual; function isApprovedForAll(address owner, address operator) public virtual view returns (bool); function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual; } // File contracts/libs/Address.sol // File: openzeppelin-solidity/contracts/utils/Address.sol pragma solidity ^0.8.9; /** * @dev Collection of functions related to the address type, */ library Address { /** * @dev Returns true if `account` is a contract. * * This test is non-exhaustive, and there may be false-negatives: during the * execution of a contract's constructor, its address will be reported as * not containing a contract. * * > It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } } // File contracts/libs/Counters.sol // File: openzeppelin-solidity/contracts/drafts/Counters.sol pragma solidity ^0.8.9; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never * directly accessed. */ library Counters { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value - 1; } } // File contracts/libs/IERC721Receiver.sol // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol pragma solidity ^0.8.9; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ abstract contract IERC721Receiver { function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) public virtual returns (bytes4); } // File contracts/libs/ERC721.sol // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol pragma solidity ^0.8.9; /** * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://eips.ethereum.org/EIPS/eip-721 */ contract ERC721 is ERC165, IERC721 { using Address for address; using Counters for Counters.Counter; // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; // Mapping from token ID to owner mapping (uint256 => address) private _tokenOwner; // Mapping from token ID to approved address mapping (uint256 => address) private _tokenApprovals; // Mapping from owner to number of owned token mapping (address => Counters.Counter) private _ownedTokensCount; // Mapping from owner to operator approvals mapping (address => mapping (address => bool)) private _operatorApprovals; bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; constructor () { // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721); } function balanceOf(address owner) public override view returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _ownedTokensCount[owner].current(); } function ownerOf(uint256 tokenId) public override view returns (address) { address owner = _tokenOwner[tokenId]; require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; } function approve(address to, uint256 tokenId) public override virtual { address owner = ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "ERC721: approve caller is not owner nor approved for all" ); _tokenApprovals[tokenId] = to; emit Approval(owner, to, tokenId); } function getApproved(uint256 tokenId) public override view returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } function setApprovalForAll(address to, bool approved) public override { require(to != msg.sender, "ERC721: approve to caller"); _operatorApprovals[msg.sender][to] = approved; emit ApprovalForAll(msg.sender, to, approved); } function isApprovedForAll(address owner, address operator) public override view returns (bool) { return _operatorApprovals[owner][operator]; } function transferFrom(address from, address to, uint256 tokenId) public override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); _transferFrom(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public override { safeTransferFrom(from, to, tokenId, ""); } function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public override { transferFrom(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } function _exists(uint256 tokenId) internal view returns (bool) { address owner = _tokenOwner[tokenId]; return owner != address(0); } function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); } function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _tokenOwner[tokenId] = to; _ownedTokensCount[to].increment(); emit Transfer(address(0), to, tokenId); } function _burn(address owner, uint256 tokenId) internal virtual { require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own"); _clearApproval(tokenId); _ownedTokensCount[owner].decrement(); _tokenOwner[tokenId] = address(0); emit Transfer(owner, address(0), tokenId); } function _burn(uint256 tokenId) internal { _burn(ownerOf(tokenId), tokenId); } function _transferFrom(address from, address to, uint256 tokenId) internal virtual { require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); require(to != address(0), "ERC721: transfer to the zero address"); _clearApproval(tokenId); _ownedTokensCount[from].decrement(); _ownedTokensCount[to].increment(); _tokenOwner[tokenId] = to; emit Transfer(from, to, tokenId); } function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) internal returns (bool) { if (!to.isContract()) { return true; } bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data); return (retval == _ERC721_RECEIVED); } function _clearApproval(uint256 tokenId) private { if (_tokenApprovals[tokenId] != address(0)) { _tokenApprovals[tokenId] = address(0); } } } // File contracts/libs/IERC721Enumerable.sol // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Enumerable.sol pragma solidity ^0.8.9; abstract contract IERC721Enumerable is IERC721 { function totalSupply() public virtual view returns (uint256); function tokenOfOwnerByIndex(address owner, uint256 index) public virtual view returns (uint256 tokenId); function tokenByIndex(uint256 index) public virtual view returns (uint256); } // File contracts/libs/ERC721Enumerable.sol // File: openzeppelin-solidity/contracts/token/ERC721/ERC721Enumerable.sol pragma solidity ^0.8.9; contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable { // Mapping from owner to list of owned token IDs mapping(address => uint256[]) private _ownedTokens; // Mapping from token ID to index of the owner tokens list mapping(uint256 => uint256) private _ownedTokensIndex; // Array with all token ids, used for enumeration uint256[] private _allTokens; // Mapping from token id to position in the allTokens array mapping(uint256 => uint256) private _allTokensIndex; bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; constructor () { // register the supported interface to conform to ERC721Enumerable via ERC165 _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); } function tokenOfOwnerByIndex(address owner, uint256 index) public override view returns (uint256) { require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); return _ownedTokens[owner][index]; } function totalSupply() public override view returns (uint256) { return _allTokens.length; } function tokenByIndex(uint256 index) public override view returns (uint256) { require(index < totalSupply(), "ERC721Enumerable: global index out of bounds"); return _allTokens[index]; } function _transferFrom(address from, address to, uint256 tokenId) override internal { super._transferFrom(from, to, tokenId); _removeTokenFromOwnerEnumeration(from, tokenId); _addTokenToOwnerEnumeration(to, tokenId); } function _mint(address to, uint256 tokenId) override internal { super._mint(to, tokenId); _addTokenToOwnerEnumeration(to, tokenId); _addTokenToAllTokensEnumeration(tokenId); } function _burn(address owner, uint256 tokenId) override internal { super._burn(owner, tokenId); _removeTokenFromOwnerEnumeration(owner, tokenId); // Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund _ownedTokensIndex[tokenId] = 0; _removeTokenFromAllTokensEnumeration(tokenId); } function _tokensOfOwner(address owner) internal view returns (uint256[] storage) { return _ownedTokens[owner]; } function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { _ownedTokensIndex[tokenId] = _ownedTokens[to].length; _ownedTokens[to].push(tokenId); } function _addTokenToAllTokensEnumeration(uint256 tokenId) private { _allTokensIndex[tokenId] = _allTokens.length; _allTokens.push(tokenId); } function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = _ownedTokens[from].length - 1; uint256 tokenIndex = _ownedTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary if (tokenIndex != lastTokenIndex) { uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index } // This also deletes the contents at the last position of the array _ownedTokens[from].pop(); // Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by // lastTokenId, or just over the end of the array if the token was the last one). } function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and // then delete the last slot (swap and pop). uint256 lastTokenIndex = _allTokens.length - 1; uint256 tokenIndex = _allTokensIndex[tokenId]; // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding // an 'if' statement (like in _removeTokenFromOwnerEnumeration) uint256 lastTokenId = _allTokens[lastTokenIndex]; _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index // This also deletes the contents at the last position of the array _allTokens.pop(); _allTokensIndex[tokenId] = 0; } } // File contracts/libs/CustomERC721Metadata.sol // File: contracts/CustomERC721Metadata.sol pragma solidity ^0.8.9; /** * ERC721 base contract without the concept of tokenUri as this is managed by the parent */ contract CustomERC721Metadata is ERC721Enumerable { // Token name string private _name; // Token symbol string private _symbol; bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; /** * @dev Constructor function */ constructor (string memory nm, string memory sym) { _name = nm; _symbol = sym; // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721_METADATA); } /** * @dev Gets the token name * @return string representing the token name */ function name() external view returns (string memory) { return _name; } /** * @dev Gets the token symbol * @return string representing the token symbol */ function symbol() external view returns (string memory) { return _symbol; } } pragma solidity ^0.8.9; interface NtentTokenUri { function tokenUri(uint256 _tokenId) external view returns(string memory); } contract NtentArt is CustomERC721Metadata { event Mint( address indexed _to, uint256 indexed _tokenId, uint256 indexed _projectId ); struct Project { string name; string artist; string description; string website; string license; address purchaseContract; address dataContract; address tokenUriContract; bool acceptsMintPass; uint256 mintPassProjectId; bool dynamic; string projectBaseURI; string projectBaseIpfsURI; uint256 invocations; uint256 maxInvocations; string scriptJSON; mapping(uint256 => string) scripts; uint scriptCount; uint256 tokensBurned; string ipfsHash; bool useHashString; bool useIpfs; bool active; bool locked; bool paused; } uint256 constant ONE_MILLION = 1_000_000; mapping(uint256 => Project) projects; //All financial functions are stripped from struct for visibility mapping(uint256 => address) public projectIdToArtistAddress; mapping(uint256 => uint256) public projectIdToPricePerTokenInWei; address public ntentAddress; uint256 public ntentPercentage = 10; mapping(uint256 => string) public staticIpfsImageLink; mapping(uint256 => uint256) public tokenIdToProjectId; mapping(uint256 => uint256[]) internal projectIdToTokenIds; mapping(uint256 => bytes32) public tokenIdToHash; mapping(bytes32 => uint256) public hashToTokenId; address public admin; mapping(address => bool) public isRainbowlisted; mapping(address => bool) public isMintRainbowlisted; uint256 public nextProjectId = 1; modifier onlyValidTokenId(uint256 _tokenId) { require(_exists(_tokenId), "Token ID not exists"); _; } modifier onlyUnlocked(uint256 _projectId) { require(!projects[_projectId].locked, "Only if unlocked"); _; } modifier onlyArtist(uint256 _projectId) { require(msg.sender == projectIdToArtistAddress[_projectId], "Only artist"); _; } modifier onlyAdmin() { require(msg.sender == admin, "Only admin"); _; } modifier onlyRainbowlisted() { require(isRainbowlisted[msg.sender], "Only Rainbowlisted"); _; } modifier onlyArtistOrRainbowlisted(uint256 _projectId) { require(isRainbowlisted[msg.sender] || msg.sender == projectIdToArtistAddress[_projectId], "Only artist or Rainbowlisted"); _; } constructor(string memory _tokenName, string memory _tokenSymbol) CustomERC721Metadata(_tokenName, _tokenSymbol) { admin = msg.sender; isRainbowlisted[msg.sender] = true; ntentAddress = msg.sender; } function concatenate(string memory a,string memory b) public pure returns (string memory){ return string(abi.encodePacked(a,b)); } function uint2str(uint _i) internal pure returns (string memory _uintAsString) { if (_i == 0) { return "0"; } uint j = _i; uint len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint k = len; while (_i != 0) { k = k-1; uint8 temp = (48 + uint8(_i - _i / 10 * 10)); bytes1 b1 = bytes1(temp); bstr[k] = b1; _i /= 10; } return string(bstr); } function mint(address _to, uint256 _projectId, address _by) external returns (uint256 _tokenId) { require(isMintRainbowlisted[msg.sender], "Must mint from Rainbowlisted minter"); require(projects[_projectId].invocations + 1 <= projects[_projectId].maxInvocations, "Exceeds max invocations"); require(projects[_projectId].active || _by == projectIdToArtistAddress[_projectId], "Proj must exist and be active"); require(!projects[_projectId].paused || _by == projectIdToArtistAddress[_projectId], "Purchases are paused"); uint256 tokenId = _mintToken(_to, _projectId); return tokenId; } function _mintToken(address _to, uint256 _projectId) internal returns (uint256 _tokenId) { uint256 tokenIdToBe = (_projectId * ONE_MILLION) + projects[_projectId].invocations; projects[_projectId].invocations = projects[_projectId].invocations + 1; bytes32 hash = keccak256(abi.encodePacked(projects[_projectId].invocations, block.number, blockhash(block.number - 1), msg.sender, _tokenId)); tokenIdToHash[tokenIdToBe]=hash; hashToTokenId[hash] = tokenIdToBe; _mint(_to, tokenIdToBe); tokenIdToProjectId[tokenIdToBe] = _projectId; projectIdToTokenIds[_projectId].push(tokenIdToBe); emit Mint(_to, tokenIdToBe, _projectId); return tokenIdToBe; } function burn(address ownerAddress, uint256 tokenId) external returns(uint256 _tokenId) { require(isMintRainbowlisted[msg.sender], "Must burn from Rainbowlisted minter"); _burn(ownerAddress, tokenId); projects[tokenIdToProjectId[tokenId]].tokensBurned = projects[tokenIdToProjectId[tokenId]].tokensBurned + 1; return tokenId; } function updateNtentAddress(address _ntentAddress) public onlyAdmin { ntentAddress = _ntentAddress; } function updateNtentPercentage(uint256 _ntentPercentage) public onlyAdmin { require(_ntentPercentage <= 50, "Max of 50%"); ntentPercentage = _ntentPercentage; } function addRainbowlisted(address _address) public onlyAdmin { isRainbowlisted[_address] = true; } function removeRainbowlisted(address _address) public onlyAdmin { isRainbowlisted[_address] = false; } function addMintRainbowlisted(address _address) public onlyAdmin { isMintRainbowlisted[_address] = true; } function removeMintRainbowlisted(address _address) public onlyAdmin { isMintRainbowlisted[_address] = false; } function getPricePerTokenInWei(uint256 _projectId) public view returns (uint256 price) { return projectIdToPricePerTokenInWei[_projectId]; } function toggleProjectIsLocked(uint256 _projectId) public onlyRainbowlisted onlyUnlocked(_projectId) { projects[_projectId].locked = true; } function toggleProjectIsActive(uint256 _projectId) public onlyRainbowlisted { projects[_projectId].active = !projects[_projectId].active; } function updateProjectArtistAddress(uint256 _projectId, address _artistAddress) public onlyArtistOrRainbowlisted(_projectId) { projectIdToArtistAddress[_projectId] = _artistAddress; } function toggleProjectIsPaused(uint256 _projectId) public onlyArtistOrRainbowlisted(_projectId) { projects[_projectId].paused = !projects[_projectId].paused; } function addProject(string memory _projectName, address _artistAddress, uint256 _pricePerTokenInWei, address _purchaseContract, bool _acceptsMintPass, uint256 _mintPassProjectId, bool _dynamic) public onlyRainbowlisted { uint256 projectId = nextProjectId; projectIdToArtistAddress[projectId] = _artistAddress; projects[projectId].name = _projectName; projects[projectId].purchaseContract = _purchaseContract; projects[projectId].acceptsMintPass = _acceptsMintPass; projects[projectId].mintPassProjectId = _mintPassProjectId; projectIdToPricePerTokenInWei[projectId] = _pricePerTokenInWei; projects[projectId].paused=true; projects[projectId].tokensBurned=0; projects[projectId].dynamic=_dynamic; projects[projectId].maxInvocations = ONE_MILLION; if (!_dynamic) { projects[projectId].useHashString = false; } else { projects[projectId].useHashString = true; } nextProjectId = nextProjectId + 1; } function updateProjectPricePerTokenInWei(uint256 _projectId, uint256 _pricePerTokenInWei) onlyArtist(_projectId) public { projectIdToPricePerTokenInWei[_projectId] = _pricePerTokenInWei; } function updateProjectName(uint256 _projectId, string memory _projectName) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { projects[_projectId].name = _projectName; } function updateProjectArtistName(uint256 _projectId, string memory _projectArtistName) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { projects[_projectId].artist = _projectArtistName; } function updateProjectPurchaseContractInfo(uint256 _projectId, address _projectPurchaseContract, bool _acceptsMintPass, uint256 _mintPassProjectId) onlyUnlocked(_projectId) onlyRainbowlisted public { projects[_projectId].purchaseContract = _projectPurchaseContract; projects[_projectId].acceptsMintPass = _acceptsMintPass; projects[_projectId].mintPassProjectId = _mintPassProjectId; } function updateProjectDataContractInfo(uint256 _projectId, address _projectDataContract) onlyUnlocked(_projectId) onlyRainbowlisted public { projects[_projectId].dataContract = _projectDataContract; } function updateProjectTokenUriContractInfo(uint256 _projectId, address _projectTokenUriContract) onlyUnlocked(_projectId) onlyRainbowlisted public { projects[_projectId].tokenUriContract = _projectTokenUriContract; } function updateProjectDescription(uint256 _projectId, string memory _projectDescription) onlyArtist(_projectId) public { projects[_projectId].description = _projectDescription; } function updateProjectWebsite(uint256 _projectId, string memory _projectWebsite) onlyArtist(_projectId) public { projects[_projectId].website = _projectWebsite; } function updateProjectLicense(uint256 _projectId, string memory _projectLicense) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { projects[_projectId].license = _projectLicense; } function updateProjectMaxInvocations(uint256 _projectId, uint256 _maxInvocations) onlyArtist(_projectId) public { require((!projects[_projectId].locked || _maxInvocations<projects[_projectId].maxInvocations), "Only if unlocked"); require(_maxInvocations > projects[_projectId].invocations, "Max invocations exceeds current"); require(_maxInvocations <= ONE_MILLION, "Cannot exceed 1000000"); projects[_projectId].maxInvocations = _maxInvocations; } function toggleProjectUseHashString(uint256 _projectId) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { require(projects[_projectId].invocations == 0, "Cannot modify after token is minted."); projects[_projectId].useHashString = !projects[_projectId].useHashString; } function addProjectScript(uint256 _projectId, string memory _script) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { projects[_projectId].scripts[projects[_projectId].scriptCount] = _script; projects[_projectId].scriptCount = projects[_projectId].scriptCount + 1; } function updateProjectScript(uint256 _projectId, uint256 _scriptId, string memory _script) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { require(_scriptId < projects[_projectId].scriptCount, "scriptId out of range"); projects[_projectId].scripts[_scriptId] = _script; } function removeProjectLastScript(uint256 _projectId) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { require(projects[_projectId].scriptCount > 0, "there are no scripts to remove"); delete projects[_projectId].scripts[projects[_projectId].scriptCount - 1]; projects[_projectId].scriptCount = projects[_projectId].scriptCount + 1; } function updateProjectScriptJSON(uint256 _projectId, string memory _projectScriptJSON) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { projects[_projectId].scriptJSON = _projectScriptJSON; } function updateProjectIpfsHash(uint256 _projectId, string memory _ipfsHash) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { projects[_projectId].ipfsHash = _ipfsHash; } function updateProjectBaseURI(uint256 _projectId, string memory _newBaseURI) onlyArtist(_projectId) public { projects[_projectId].projectBaseURI = _newBaseURI; } function updateProjectBaseIpfsURI(uint256 _projectId, string memory _projectBaseIpfsURI) onlyArtist(_projectId) public { projects[_projectId].projectBaseIpfsURI = _projectBaseIpfsURI; } function toggleProjectUseIpfsForStatic(uint256 _projectId) onlyArtistOrRainbowlisted(_projectId) public { require(!projects[_projectId].dynamic, "can only set static IPFS hash for static projects"); projects[_projectId].useIpfs = !projects[_projectId].useIpfs; } function toggleProjectIsDynamic(uint256 _projectId) onlyUnlocked(_projectId) onlyArtistOrRainbowlisted(_projectId) public { require(projects[_projectId].invocations == 0, "Can not switch after a token is minted."); if (projects[_projectId].dynamic) { projects[_projectId].useHashString = false; } else { projects[_projectId].useHashString = true; } projects[_projectId].dynamic = !projects[_projectId].dynamic; } function overrideTokenDynamicImageWithIpfsLink(uint256 _tokenId, string memory _ipfsHash) onlyArtistOrRainbowlisted(tokenIdToProjectId[_tokenId]) public { staticIpfsImageLink[_tokenId] = _ipfsHash; } function clearTokenIpfsImageUri(uint256 _tokenId) onlyArtistOrRainbowlisted(tokenIdToProjectId[_tokenId]) public { delete staticIpfsImageLink[tokenIdToProjectId[_tokenId]]; } function projectDetails(uint256 _projectId) view public returns (string memory projectName, string memory artist, string memory description, string memory website, string memory license, bool dynamic) { projectName = projects[_projectId].name; artist = projects[_projectId].artist; description = projects[_projectId].description; website = projects[_projectId].website; license = projects[_projectId].license; dynamic = projects[_projectId].dynamic; } function projectTokenInfo(uint256 _projectId) view public returns (address artistAddress, uint256 pricePerTokenInWei, uint256 invocations, uint256 maxInvocations, bool active, address purchaseContract, address dataContract, address tokenUriContract, bool acceptsMintPass, uint256 mintPassProjectId) { artistAddress = projectIdToArtistAddress[_projectId]; pricePerTokenInWei = projectIdToPricePerTokenInWei[_projectId]; invocations = projects[_projectId].invocations; maxInvocations = projects[_projectId].maxInvocations; active = projects[_projectId].active; purchaseContract = projects[_projectId].purchaseContract; dataContract = projects[_projectId].dataContract; tokenUriContract = projects[_projectId].tokenUriContract; acceptsMintPass = projects[_projectId].acceptsMintPass; mintPassProjectId = projects[_projectId].mintPassProjectId; } function projectScriptInfo(uint256 _projectId) view public returns (string memory scriptJSON, uint256 scriptCount, bool useHashString, string memory ipfsHash, bool locked, bool paused) { scriptJSON = projects[_projectId].scriptJSON; scriptCount = projects[_projectId].scriptCount; useHashString = projects[_projectId].useHashString; ipfsHash = projects[_projectId].ipfsHash; locked = projects[_projectId].locked; paused = projects[_projectId].paused; } function projectScriptByIndex(uint256 _projectId, uint256 _index) view public returns (string memory){ return projects[_projectId].scripts[_index]; } function projectURIInfo(uint256 _projectId) view public returns (string memory projectBaseURI, string memory projectBaseIpfsURI, bool useIpfs) { projectBaseURI = projects[_projectId].projectBaseURI; projectBaseIpfsURI = projects[_projectId].projectBaseIpfsURI; useIpfs = projects[_projectId].useIpfs; } function projectShowAllTokens(uint _projectId) public view returns (uint256[] memory){ return projectIdToTokenIds[_projectId]; } function tokensOfOwner(address owner) external view returns (uint256[] memory) { return _tokensOfOwner(owner); } function tokenURI(uint256 _tokenId) external view onlyValidTokenId(_tokenId) returns (string memory) { //check if custom tokenUri contract, if so, use that. if(projects[tokenIdToProjectId[_tokenId]].tokenUriContract != address(0)){ NtentTokenUri ntentTokenUri = NtentTokenUri(projects[tokenIdToProjectId[_tokenId]].tokenUriContract); return ntentTokenUri.tokenUri(_tokenId); } //check if tokenId has a specified image link if (bytes(staticIpfsImageLink[_tokenId]).length > 0) { return concatenate(projects[tokenIdToProjectId[_tokenId]].projectBaseIpfsURI, staticIpfsImageLink[_tokenId]); } //check if the project has a single overall token Uri (mintpass, etc) if (!projects[tokenIdToProjectId[_tokenId]].dynamic && projects[tokenIdToProjectId[_tokenId]].useIpfs) { return concatenate(projects[tokenIdToProjectId[_tokenId]].projectBaseIpfsURI, projects[tokenIdToProjectId[_tokenId]].ipfsHash); } return concatenate(projects[tokenIdToProjectId[_tokenId]].projectBaseURI, uint2str(_tokenId)); } }