From 7ebd18031f38737fe30404514fa1b37de0d96c06 Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Fri, 17 Dec 2021 13:50:57 +0100 Subject: [PATCH 1/9] Adding NPM dependencies enable IDE intellisense --- package-lock.json | 13 +++++++++++++ package.json | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..85c204e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "hashlips_nft_contract", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@openzeppelin/contracts": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.1.tgz", + "integrity": "sha512-o+pHCf/yMLSlV5MkDQEzEQL402i6SoRnktru+0rdSxVEFZcTzzGhZCAtZjUFyKGazMSv1TilzMg+RbED1N8XHQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5b7b67c --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "hashlips_nft_contract", + "version": "1.0.0", + "description": "A simple NFT smart contract that works with the rest of the HashLips ecosystem.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/HashLips/hashlips_nft_contract.git" + }, + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/HashLips/hashlips_nft_contract/issues" + }, + "homepage": "https://github.com/HashLips/hashlips_nft_contract#readme", + "dependencies": { + "@openzeppelin/contracts": "^4.4.1" + } +} From 4dba5f0c7f5cbb57b872afdb8ebee011a341df05 Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Fri, 17 Dec 2021 13:55:58 +0100 Subject: [PATCH 2/9] Moving from ERC721Enumerable to ERC721 + Counters --- contract/SimpleNft.sol | 51 +++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/contract/SimpleNft.sol b/contract/SimpleNft.sol index 7977bfc..aec42c7 100644 --- a/contract/SimpleNft.sol +++ b/contract/SimpleNft.sol @@ -15,20 +15,31 @@ pragma solidity >=0.7.0 <0.9.0; -import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -contract NFT is ERC721Enumerable, Ownable { +contract NFT is ERC721, Ownable { using Strings for uint256; string baseURI; string public baseExtension = ".json"; uint256 public cost = 0.05 ether; - uint256 public maxSupply = 10000; + /* + * Always set this to "MAX_SUPPLY + 1", this allows to use a more + * effcient counter and have lower gas fees. + */ + uint256 public maxSupply = 10000 + 1; uint256 public maxMintAmount = 20; bool public paused = false; bool public revealed = false; string public notRevealedUri; + /* + * Using Counters instead of ETC721Enumerable to save gas. + * See https://shiny.mirror.xyz/OUampBbIz9ebEicfGnQf5At_ReMHlZy0tB4glb9xQ0E + */ + using Counters for Counters.Counter; + Counters.Counter private _nextTokenId; constructor( string memory _name, @@ -38,6 +49,9 @@ contract NFT is ERC721Enumerable, Ownable { ) ERC721(_name, _symbol) { setBaseURI(_initBaseURI); setNotRevealedURI(_initNotRevealedUri); + + // Avoid higher gas fee for the first minter by initializing the counter here. + _nextTokenId.increment(); } // internal @@ -47,34 +61,22 @@ contract NFT is ERC721Enumerable, Ownable { // public function mint(uint256 _mintAmount) public payable { - uint256 supply = totalSupply(); + uint256 nextId = _nextTokenId.current(); require(!paused); require(_mintAmount > 0); require(_mintAmount <= maxMintAmount); - require(supply + _mintAmount <= maxSupply); + require(nextId + _mintAmount <= maxSupply); if (msg.sender != owner()) { require(msg.value >= cost * _mintAmount); } for (uint256 i = 1; i <= _mintAmount; i++) { - _safeMint(msg.sender, supply + i); + _safeMint(msg.sender, _nextTokenId.current()); + _nextTokenId.increment(); } } - function walletOfOwner(address _owner) - public - view - returns (uint256[] memory) - { - uint256 ownerTokenCount = balanceOf(_owner); - uint256[] memory tokenIds = new uint256[](ownerTokenCount); - for (uint256 i; i < ownerTokenCount; i++) { - tokenIds[i] = tokenOfOwnerByIndex(_owner, i); - } - return tokenIds; - } - function tokenURI(uint256 tokenId) public view @@ -88,7 +90,7 @@ contract NFT is ERC721Enumerable, Ownable { ); if(revealed == false) { - return notRevealedUri; + return notRevealedUri; } string memory currentBaseURI = _baseURI(); @@ -97,9 +99,16 @@ contract NFT is ERC721Enumerable, Ownable { : ""; } + /** + * @dev See {IERC721Enumerable-totalSupply}. + */ + function totalSupply() external view returns (uint256) { + return _nextTokenId.current() - 1; + } + //only owner function reveal() public onlyOwner { - revealed = true; + revealed = true; } function setCost(uint256 _newCost) public onlyOwner { From 12c9d7c08b01a68b671d76117da19585b58b261e Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Fri, 17 Dec 2021 16:34:46 +0100 Subject: [PATCH 3/9] New implementation of "walletOfOwner" --- contract/SimpleNft.sol | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/contract/SimpleNft.sol b/contract/SimpleNft.sol index aec42c7..e7429e8 100644 --- a/contract/SimpleNft.sol +++ b/contract/SimpleNft.sol @@ -77,6 +77,28 @@ contract NFT is ERC721, Ownable { } } + function walletOfOwner(address _owner) + public + view + returns (uint256[] memory) + { + uint256 ownerTokenCount = balanceOf(_owner); + uint256[] memory ownedTokens = new uint256[](ownerTokenCount); + uint256 currentTokenIndex = 1; + + for (uint256 i; i < ownerTokenCount; i++) { + address currentTokenOwner = ownerOf(currentTokenIndex); + + if (currentTokenOwner == _owner) { + ownedTokens[i] = currentTokenIndex; + } + + currentTokenIndex++; + } + + return ownedTokens; + } + function tokenURI(uint256 tokenId) public view From 2e6f28295c29f94e4ed05343928e5340fb9f96f3 Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Fri, 17 Dec 2021 18:39:12 +0100 Subject: [PATCH 4/9] Adding OpenSea Whitelisting feature --- contract/SimpleNft.sol | 61 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/contract/SimpleNft.sol b/contract/SimpleNft.sol index e7429e8..f2e826a 100644 --- a/contract/SimpleNft.sol +++ b/contract/SimpleNft.sol @@ -19,6 +19,17 @@ import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; +// [BEGIN] OpenSea interfaces (see: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/ERC721Tradable.sol) +contract OwnableDelegateProxy {} + +/** + * Used to delegate ownership of a contract to another address, to save on unneeded transactions to approve contract use for users + */ +contract ProxyRegistry { + mapping(address => OwnableDelegateProxy) public proxies; +} +// [END] OpenSea interfaces + contract NFT is ERC721, Ownable { using Strings for uint256; @@ -40,18 +51,21 @@ contract NFT is ERC721, Ownable { */ using Counters for Counters.Counter; Counters.Counter private _nextTokenId; + address openSeaProxyRegistryAddress; constructor( string memory _name, string memory _symbol, string memory _initBaseURI, - string memory _initNotRevealedUri + string memory _initNotRevealedUri, + address _openSeaProxyRegistryAddress ) ERC721(_name, _symbol) { setBaseURI(_initBaseURI); setNotRevealedURI(_initNotRevealedUri); // Avoid higher gas fee for the first minter by initializing the counter here. _nextTokenId.increment(); + openSeaProxyRegistryAddress = _openSeaProxyRegistryAddress; } // internal @@ -128,6 +142,51 @@ contract NFT is ERC721, Ownable { return _nextTokenId.current() - 1; } + /** + * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings. + */ + function isApprovedForAll(address owner, address operator) + override + public + view + returns (bool) + { + // Whitelist OpenSea proxy contract for easy trading. + ProxyRegistry proxyRegistry = ProxyRegistry(openSeaProxyRegistryAddress); + if (address(proxyRegistry.proxies(owner)) == operator) { + return true; + } + + return super.isApprovedForAll(owner, operator); + } + + /** + * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea. + */ + function _msgSender() + internal + override + view + returns (address sender) + { + // See: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/common/meta-transactions/ContentMixin.sol + if (msg.sender == address(this)) { + bytes memory array = msg.data; + uint256 index = msg.data.length; + assembly { + // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those. + sender := and( + mload(add(array, index)), + 0xffffffffffffffffffffffffffffffffffffffff + ) + } + } else { + sender = payable(msg.sender); + } + + return sender; + } + //only owner function reveal() public onlyOwner { revealed = true; From 9d27cfa465684abccc1f1ca343e2ec61d57315d5 Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Sat, 18 Dec 2021 02:18:42 +0100 Subject: [PATCH 5/9] Fixing broken walletOfOwner implementation --- contract/SimpleNft.sol | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contract/SimpleNft.sol b/contract/SimpleNft.sol index f2e826a..4ecc72f 100644 --- a/contract/SimpleNft.sol +++ b/contract/SimpleNft.sol @@ -98,13 +98,17 @@ contract NFT is ERC721, Ownable { { uint256 ownerTokenCount = balanceOf(_owner); uint256[] memory ownedTokens = new uint256[](ownerTokenCount); - uint256 currentTokenIndex = 1; + uint256 currentTokenIndex = 1; // loop through all tokens from 1 to (maxSupply - 1) + uint256 ownedTokenIndex = 0; // index for returned array - for (uint256 i; i < ownerTokenCount; i++) { + // Early return if all tokens owned by this address have been found + while (ownedTokenIndex < ownerTokenCount && currentTokenIndex < maxSupply) { address currentTokenOwner = ownerOf(currentTokenIndex); if (currentTokenOwner == _owner) { - ownedTokens[i] = currentTokenIndex; + ownedTokens[ownedTokenIndex] = currentTokenIndex; + + ownedTokenIndex++; } currentTokenIndex++; From 9ba7ed3d9f7f1b3a097f767dc8ef3b753b91be74 Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Wed, 5 Jan 2022 12:05:29 +0100 Subject: [PATCH 6/9] Reverting back the original contract and creating a new a "lower-gas" version --- contract/SimpleNft.sol | 120 ++--------------- contract/SimpleNftLowerGas.sol | 238 +++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 107 deletions(-) create mode 100644 contract/SimpleNftLowerGas.sol diff --git a/contract/SimpleNft.sol b/contract/SimpleNft.sol index 4ecc72f..7977bfc 100644 --- a/contract/SimpleNft.sol +++ b/contract/SimpleNft.sol @@ -15,57 +15,29 @@ pragma solidity >=0.7.0 <0.9.0; -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; -import "@openzeppelin/contracts/utils/Counters.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -// [BEGIN] OpenSea interfaces (see: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/ERC721Tradable.sol) -contract OwnableDelegateProxy {} - -/** - * Used to delegate ownership of a contract to another address, to save on unneeded transactions to approve contract use for users - */ -contract ProxyRegistry { - mapping(address => OwnableDelegateProxy) public proxies; -} -// [END] OpenSea interfaces - -contract NFT is ERC721, Ownable { +contract NFT is ERC721Enumerable, Ownable { using Strings for uint256; string baseURI; string public baseExtension = ".json"; uint256 public cost = 0.05 ether; - /* - * Always set this to "MAX_SUPPLY + 1", this allows to use a more - * effcient counter and have lower gas fees. - */ - uint256 public maxSupply = 10000 + 1; + uint256 public maxSupply = 10000; uint256 public maxMintAmount = 20; bool public paused = false; bool public revealed = false; string public notRevealedUri; - /* - * Using Counters instead of ETC721Enumerable to save gas. - * See https://shiny.mirror.xyz/OUampBbIz9ebEicfGnQf5At_ReMHlZy0tB4glb9xQ0E - */ - using Counters for Counters.Counter; - Counters.Counter private _nextTokenId; - address openSeaProxyRegistryAddress; constructor( string memory _name, string memory _symbol, string memory _initBaseURI, - string memory _initNotRevealedUri, - address _openSeaProxyRegistryAddress + string memory _initNotRevealedUri ) ERC721(_name, _symbol) { setBaseURI(_initBaseURI); setNotRevealedURI(_initNotRevealedUri); - - // Avoid higher gas fee for the first minter by initializing the counter here. - _nextTokenId.increment(); - openSeaProxyRegistryAddress = _openSeaProxyRegistryAddress; } // internal @@ -75,19 +47,18 @@ contract NFT is ERC721, Ownable { // public function mint(uint256 _mintAmount) public payable { - uint256 nextId = _nextTokenId.current(); + uint256 supply = totalSupply(); require(!paused); require(_mintAmount > 0); require(_mintAmount <= maxMintAmount); - require(nextId + _mintAmount <= maxSupply); + require(supply + _mintAmount <= maxSupply); if (msg.sender != owner()) { require(msg.value >= cost * _mintAmount); } for (uint256 i = 1; i <= _mintAmount; i++) { - _safeMint(msg.sender, _nextTokenId.current()); - _nextTokenId.increment(); + _safeMint(msg.sender, supply + i); } } @@ -97,24 +68,11 @@ contract NFT is ERC721, Ownable { returns (uint256[] memory) { uint256 ownerTokenCount = balanceOf(_owner); - uint256[] memory ownedTokens = new uint256[](ownerTokenCount); - uint256 currentTokenIndex = 1; // loop through all tokens from 1 to (maxSupply - 1) - uint256 ownedTokenIndex = 0; // index for returned array - - // Early return if all tokens owned by this address have been found - while (ownedTokenIndex < ownerTokenCount && currentTokenIndex < maxSupply) { - address currentTokenOwner = ownerOf(currentTokenIndex); - - if (currentTokenOwner == _owner) { - ownedTokens[ownedTokenIndex] = currentTokenIndex; - - ownedTokenIndex++; - } - - currentTokenIndex++; + uint256[] memory tokenIds = new uint256[](ownerTokenCount); + for (uint256 i; i < ownerTokenCount; i++) { + tokenIds[i] = tokenOfOwnerByIndex(_owner, i); } - - return ownedTokens; + return tokenIds; } function tokenURI(uint256 tokenId) @@ -130,7 +88,7 @@ contract NFT is ERC721, Ownable { ); if(revealed == false) { - return notRevealedUri; + return notRevealedUri; } string memory currentBaseURI = _baseURI(); @@ -139,61 +97,9 @@ contract NFT is ERC721, Ownable { : ""; } - /** - * @dev See {IERC721Enumerable-totalSupply}. - */ - function totalSupply() external view returns (uint256) { - return _nextTokenId.current() - 1; - } - - /** - * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings. - */ - function isApprovedForAll(address owner, address operator) - override - public - view - returns (bool) - { - // Whitelist OpenSea proxy contract for easy trading. - ProxyRegistry proxyRegistry = ProxyRegistry(openSeaProxyRegistryAddress); - if (address(proxyRegistry.proxies(owner)) == operator) { - return true; - } - - return super.isApprovedForAll(owner, operator); - } - - /** - * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea. - */ - function _msgSender() - internal - override - view - returns (address sender) - { - // See: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/common/meta-transactions/ContentMixin.sol - if (msg.sender == address(this)) { - bytes memory array = msg.data; - uint256 index = msg.data.length; - assembly { - // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those. - sender := and( - mload(add(array, index)), - 0xffffffffffffffffffffffffffffffffffffffff - ) - } - } else { - sender = payable(msg.sender); - } - - return sender; - } - //only owner function reveal() public onlyOwner { - revealed = true; + revealed = true; } function setCost(uint256 _newCost) public onlyOwner { diff --git a/contract/SimpleNftLowerGas.sol b/contract/SimpleNftLowerGas.sol new file mode 100644 index 0000000..4ecc72f --- /dev/null +++ b/contract/SimpleNftLowerGas.sol @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: MIT + +// Amended by HashLips +/** + !Disclaimer! + These contracts have been used to create tutorials, + and was created for the purpose to teach people + how to create smart contracts on the blockchain. + please review this code on your own before using any of + the following code for production. + HashLips will not be liable in any way if for the use + of the code. That being said, the code has been tested + to the best of the developers' knowledge to work as intended. +*/ + +pragma solidity >=0.7.0 <0.9.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +// [BEGIN] OpenSea interfaces (see: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/ERC721Tradable.sol) +contract OwnableDelegateProxy {} + +/** + * Used to delegate ownership of a contract to another address, to save on unneeded transactions to approve contract use for users + */ +contract ProxyRegistry { + mapping(address => OwnableDelegateProxy) public proxies; +} +// [END] OpenSea interfaces + +contract NFT is ERC721, Ownable { + using Strings for uint256; + + string baseURI; + string public baseExtension = ".json"; + uint256 public cost = 0.05 ether; + /* + * Always set this to "MAX_SUPPLY + 1", this allows to use a more + * effcient counter and have lower gas fees. + */ + uint256 public maxSupply = 10000 + 1; + uint256 public maxMintAmount = 20; + bool public paused = false; + bool public revealed = false; + string public notRevealedUri; + /* + * Using Counters instead of ETC721Enumerable to save gas. + * See https://shiny.mirror.xyz/OUampBbIz9ebEicfGnQf5At_ReMHlZy0tB4glb9xQ0E + */ + using Counters for Counters.Counter; + Counters.Counter private _nextTokenId; + address openSeaProxyRegistryAddress; + + constructor( + string memory _name, + string memory _symbol, + string memory _initBaseURI, + string memory _initNotRevealedUri, + address _openSeaProxyRegistryAddress + ) ERC721(_name, _symbol) { + setBaseURI(_initBaseURI); + setNotRevealedURI(_initNotRevealedUri); + + // Avoid higher gas fee for the first minter by initializing the counter here. + _nextTokenId.increment(); + openSeaProxyRegistryAddress = _openSeaProxyRegistryAddress; + } + + // internal + function _baseURI() internal view virtual override returns (string memory) { + return baseURI; + } + + // public + function mint(uint256 _mintAmount) public payable { + uint256 nextId = _nextTokenId.current(); + require(!paused); + require(_mintAmount > 0); + require(_mintAmount <= maxMintAmount); + require(nextId + _mintAmount <= maxSupply); + + if (msg.sender != owner()) { + require(msg.value >= cost * _mintAmount); + } + + for (uint256 i = 1; i <= _mintAmount; i++) { + _safeMint(msg.sender, _nextTokenId.current()); + _nextTokenId.increment(); + } + } + + function walletOfOwner(address _owner) + public + view + returns (uint256[] memory) + { + uint256 ownerTokenCount = balanceOf(_owner); + uint256[] memory ownedTokens = new uint256[](ownerTokenCount); + uint256 currentTokenIndex = 1; // loop through all tokens from 1 to (maxSupply - 1) + uint256 ownedTokenIndex = 0; // index for returned array + + // Early return if all tokens owned by this address have been found + while (ownedTokenIndex < ownerTokenCount && currentTokenIndex < maxSupply) { + address currentTokenOwner = ownerOf(currentTokenIndex); + + if (currentTokenOwner == _owner) { + ownedTokens[ownedTokenIndex] = currentTokenIndex; + + ownedTokenIndex++; + } + + currentTokenIndex++; + } + + return ownedTokens; + } + + function tokenURI(uint256 tokenId) + public + view + virtual + override + returns (string memory) + { + require( + _exists(tokenId), + "ERC721Metadata: URI query for nonexistent token" + ); + + if(revealed == false) { + return notRevealedUri; + } + + string memory currentBaseURI = _baseURI(); + return bytes(currentBaseURI).length > 0 + ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension)) + : ""; + } + + /** + * @dev See {IERC721Enumerable-totalSupply}. + */ + function totalSupply() external view returns (uint256) { + return _nextTokenId.current() - 1; + } + + /** + * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings. + */ + function isApprovedForAll(address owner, address operator) + override + public + view + returns (bool) + { + // Whitelist OpenSea proxy contract for easy trading. + ProxyRegistry proxyRegistry = ProxyRegistry(openSeaProxyRegistryAddress); + if (address(proxyRegistry.proxies(owner)) == operator) { + return true; + } + + return super.isApprovedForAll(owner, operator); + } + + /** + * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea. + */ + function _msgSender() + internal + override + view + returns (address sender) + { + // See: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/common/meta-transactions/ContentMixin.sol + if (msg.sender == address(this)) { + bytes memory array = msg.data; + uint256 index = msg.data.length; + assembly { + // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those. + sender := and( + mload(add(array, index)), + 0xffffffffffffffffffffffffffffffffffffffff + ) + } + } else { + sender = payable(msg.sender); + } + + return sender; + } + + //only owner + function reveal() public onlyOwner { + revealed = true; + } + + function setCost(uint256 _newCost) public onlyOwner { + cost = _newCost; + } + + function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner { + maxMintAmount = _newmaxMintAmount; + } + + function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner { + notRevealedUri = _notRevealedURI; + } + + function setBaseURI(string memory _newBaseURI) public onlyOwner { + baseURI = _newBaseURI; + } + + function setBaseExtension(string memory _newBaseExtension) public onlyOwner { + baseExtension = _newBaseExtension; + } + + function pause(bool _state) public onlyOwner { + paused = _state; + } + + function withdraw() public payable onlyOwner { + // This will pay HashLips 5% of the initial sale. + // You can remove this if you want, or keep it in to support HashLips and his channel. + // ============================================================================= + (bool hs, ) = payable(0x943590A42C27D08e3744202c4Ae5eD55c2dE240D).call{value: address(this).balance * 5 / 100}(""); + require(hs); + // ============================================================================= + + // This will payout the owner 95% of the contract balance. + // Do not remove this otherwise you will not be able to withdraw the funds. + // ============================================================================= + (bool os, ) = payable(owner()).call{value: address(this).balance}(""); + require(os); + // ============================================================================= + } +} From dcb8694141110488f94b7ca8ba0060cf8acfbfcf Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Wed, 5 Jan 2022 12:15:44 +0100 Subject: [PATCH 7/9] Removing NPM files --- package-lock.json | 13 ------------- package.json | 22 ---------------------- 2 files changed, 35 deletions(-) delete mode 100644 package-lock.json delete mode 100644 package.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 85c204e..0000000 --- a/package-lock.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "hashlips_nft_contract", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@openzeppelin/contracts": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.1.tgz", - "integrity": "sha512-o+pHCf/yMLSlV5MkDQEzEQL402i6SoRnktru+0rdSxVEFZcTzzGhZCAtZjUFyKGazMSv1TilzMg+RbED1N8XHQ==" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 5b7b67c..0000000 --- a/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "hashlips_nft_contract", - "version": "1.0.0", - "description": "A simple NFT smart contract that works with the rest of the HashLips ecosystem.", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/HashLips/hashlips_nft_contract.git" - }, - "author": "", - "license": "MIT", - "bugs": { - "url": "https://github.com/HashLips/hashlips_nft_contract/issues" - }, - "homepage": "https://github.com/HashLips/hashlips_nft_contract#readme", - "dependencies": { - "@openzeppelin/contracts": "^4.4.1" - } -} From d091e2566b0a02b651bc9896b664e306d143dee3 Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Thu, 6 Jan 2022 23:19:36 +0100 Subject: [PATCH 8/9] Updating contract code after live tests --- contract/SimpleNftLowerGas.sol | 225 +++++++++++---------------------- 1 file changed, 76 insertions(+), 149 deletions(-) diff --git a/contract/SimpleNftLowerGas.sol b/contract/SimpleNftLowerGas.sol index 4ecc72f..466a957 100644 --- a/contract/SimpleNftLowerGas.sol +++ b/contract/SimpleNftLowerGas.sol @@ -8,7 +8,7 @@ how to create smart contracts on the blockchain. please review this code on your own before using any of the following code for production. - HashLips will not be liable in any way if for the use + HashLips will not be liable in any way for the use of the code. That being said, the code has been tested to the best of the developers' knowledge to work as intended. */ @@ -19,76 +19,46 @@ import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -// [BEGIN] OpenSea interfaces (see: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/ERC721Tradable.sol) -contract OwnableDelegateProxy {} - -/** - * Used to delegate ownership of a contract to another address, to save on unneeded transactions to approve contract use for users - */ -contract ProxyRegistry { - mapping(address => OwnableDelegateProxy) public proxies; -} -// [END] OpenSea interfaces - -contract NFT is ERC721, Ownable { +contract SimpleNftLowerGas is ERC721, Ownable { using Strings for uint256; - - string baseURI; - string public baseExtension = ".json"; - uint256 public cost = 0.05 ether; - /* - * Always set this to "MAX_SUPPLY + 1", this allows to use a more - * effcient counter and have lower gas fees. - */ - uint256 public maxSupply = 10000 + 1; - uint256 public maxMintAmount = 20; - bool public paused = false; - bool public revealed = false; - string public notRevealedUri; - /* - * Using Counters instead of ETC721Enumerable to save gas. - * See https://shiny.mirror.xyz/OUampBbIz9ebEicfGnQf5At_ReMHlZy0tB4glb9xQ0E - */ using Counters for Counters.Counter; - Counters.Counter private _nextTokenId; - address openSeaProxyRegistryAddress; - constructor( - string memory _name, - string memory _symbol, - string memory _initBaseURI, - string memory _initNotRevealedUri, - address _openSeaProxyRegistryAddress - ) ERC721(_name, _symbol) { - setBaseURI(_initBaseURI); - setNotRevealedURI(_initNotRevealedUri); + Counters.Counter private supply; + + string public uriPrefix = ""; + string public uriSuffix = ".json"; + string public hiddenMetadataUri; + + uint256 public cost = 0.01 ether; + uint256 public maxSupply = 10000; + uint256 public maxMintAmountPerTx = 5; - // Avoid higher gas fee for the first minter by initializing the counter here. - _nextTokenId.increment(); - openSeaProxyRegistryAddress = _openSeaProxyRegistryAddress; + bool public paused = true; + bool public revealed = false; + + constructor() ERC721("NAME", "SYMBOL") { + setHiddenMetadataUri("ipfs://__CID__/hidden.json"); } - // internal - function _baseURI() internal view virtual override returns (string memory) { - return baseURI; + modifier mintCompliance(uint256 _mintAmount) { + require(_mintAmount > 0 && _mintAmount <= maxMintAmountPerTx, "Invalid mint amount!"); + require(supply.current() + _mintAmount <= maxSupply, "Max supply exceeded!"); + _; } - // public - function mint(uint256 _mintAmount) public payable { - uint256 nextId = _nextTokenId.current(); - require(!paused); - require(_mintAmount > 0); - require(_mintAmount <= maxMintAmount); - require(nextId + _mintAmount <= maxSupply); + function totalSupply() public view returns (uint256) { + return supply.current(); + } - if (msg.sender != owner()) { - require(msg.value >= cost * _mintAmount); - } + function mint(uint256 _mintAmount) public payable mintCompliance(_mintAmount) { + require(!paused, "The contract is paused!"); + require(msg.value >= cost * _mintAmount, "Insufficient funds!"); - for (uint256 i = 1; i <= _mintAmount; i++) { - _safeMint(msg.sender, _nextTokenId.current()); - _nextTokenId.increment(); - } + _mintLoop(msg.sender, _mintAmount); + } + + function mintForAddress(uint256 _mintAmount, address _receiver) public mintCompliance(_mintAmount) onlyOwner { + _mintLoop(_receiver, _mintAmount); } function walletOfOwner(address _owner) @@ -97,27 +67,26 @@ contract NFT is ERC721, Ownable { returns (uint256[] memory) { uint256 ownerTokenCount = balanceOf(_owner); - uint256[] memory ownedTokens = new uint256[](ownerTokenCount); - uint256 currentTokenIndex = 1; // loop through all tokens from 1 to (maxSupply - 1) - uint256 ownedTokenIndex = 0; // index for returned array - - // Early return if all tokens owned by this address have been found - while (ownedTokenIndex < ownerTokenCount && currentTokenIndex < maxSupply) { - address currentTokenOwner = ownerOf(currentTokenIndex); - + uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount); + uint256 currentTokenId = 1; + uint256 ownedTokenIndex = 0; + + while (ownedTokenIndex < ownerTokenCount && currentTokenId <= maxSupply) { + address currentTokenOwner = ownerOf(currentTokenId); + if (currentTokenOwner == _owner) { - ownedTokens[ownedTokenIndex] = currentTokenIndex; + ownedTokenIds[ownedTokenIndex] = currentTokenId; ownedTokenIndex++; } - currentTokenIndex++; + currentTokenId++; } - return ownedTokens; + return ownedTokenIds; } - function tokenURI(uint256 tokenId) + function tokenURI(uint256 _tokenId) public view virtual @@ -125,114 +94,72 @@ contract NFT is ERC721, Ownable { returns (string memory) { require( - _exists(tokenId), + _exists(_tokenId), "ERC721Metadata: URI query for nonexistent token" ); - - if(revealed == false) { - return notRevealedUri; + + if (revealed == false) { + return hiddenMetadataUri; } string memory currentBaseURI = _baseURI(); return bytes(currentBaseURI).length > 0 - ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension)) + ? string(abi.encodePacked(currentBaseURI, _tokenId.toString(), uriSuffix)) : ""; } - /** - * @dev See {IERC721Enumerable-totalSupply}. - */ - function totalSupply() external view returns (uint256) { - return _nextTokenId.current() - 1; + function setRevealed(bool _state) public onlyOwner { + revealed = _state; } - /** - * Override isApprovedForAll to whitelist user's OpenSea proxy accounts to enable gas-less listings. - */ - function isApprovedForAll(address owner, address operator) - override - public - view - returns (bool) - { - // Whitelist OpenSea proxy contract for easy trading. - ProxyRegistry proxyRegistry = ProxyRegistry(openSeaProxyRegistryAddress); - if (address(proxyRegistry.proxies(owner)) == operator) { - return true; - } - - return super.isApprovedForAll(owner, operator); + function setCost(uint256 _cost) public onlyOwner { + cost = _cost; } - /** - * This is used instead of msg.sender as transactions won't be sent by the original token owner, but by OpenSea. - */ - function _msgSender() - internal - override - view - returns (address sender) - { - // See: https://github.com/ProjectOpenSea/opensea-creatures/blob/master/contracts/common/meta-transactions/ContentMixin.sol - if (msg.sender == address(this)) { - bytes memory array = msg.data; - uint256 index = msg.data.length; - assembly { - // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those. - sender := and( - mload(add(array, index)), - 0xffffffffffffffffffffffffffffffffffffffff - ) - } - } else { - sender = payable(msg.sender); - } - - return sender; + function setMaxMintAmountPerTx(uint256 _maxMintAmountPerTx) public onlyOwner { + maxMintAmountPerTx = _maxMintAmountPerTx; } - //only owner - function reveal() public onlyOwner { - revealed = true; - } - - function setCost(uint256 _newCost) public onlyOwner { - cost = _newCost; + function setHiddenMetadataUri(string memory _hiddenMetadataUri) public onlyOwner { + hiddenMetadataUri = _hiddenMetadataUri; } - function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner { - maxMintAmount = _newmaxMintAmount; - } - - function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner { - notRevealedUri = _notRevealedURI; + function setUriPrefix(string memory _uriPrefix) public onlyOwner { + uriPrefix = _uriPrefix; } - function setBaseURI(string memory _newBaseURI) public onlyOwner { - baseURI = _newBaseURI; + function setUriSuffix(string memory _uriSuffix) public onlyOwner { + uriSuffix = _uriSuffix; } - function setBaseExtension(string memory _newBaseExtension) public onlyOwner { - baseExtension = _newBaseExtension; - } - - function pause(bool _state) public onlyOwner { + function setPaused(bool _state) public onlyOwner { paused = _state; } - - function withdraw() public payable onlyOwner { + + function withdraw() public onlyOwner { // This will pay HashLips 5% of the initial sale. // You can remove this if you want, or keep it in to support HashLips and his channel. // ============================================================================= (bool hs, ) = payable(0x943590A42C27D08e3744202c4Ae5eD55c2dE240D).call{value: address(this).balance * 5 / 100}(""); require(hs); // ============================================================================= - - // This will payout the owner 95% of the contract balance. + + // This will transfer the remaining contract balance to the owner. // Do not remove this otherwise you will not be able to withdraw the funds. // ============================================================================= (bool os, ) = payable(owner()).call{value: address(this).balance}(""); require(os); // ============================================================================= } -} + + function _mintLoop(address _receiver, uint256 _mintAmount) internal { + for (uint256 i = 0; i < _mintAmount; i++) { + supply.increment(); + _safeMint(_receiver, supply.current()); + } + } + + function _baseURI() internal view virtual override returns (string memory) { + return uriPrefix; + } +} \ No newline at end of file From 7c2922c783b5ed491c4d5246c0473824108bf33f Mon Sep 17 00:00:00 2001 From: Marco Lipparini Date: Thu, 6 Jan 2022 23:27:43 +0100 Subject: [PATCH 9/9] Minor fixes --- contract/SimpleNftLowerGas.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contract/SimpleNftLowerGas.sol b/contract/SimpleNftLowerGas.sol index 466a957..163e0f2 100644 --- a/contract/SimpleNftLowerGas.sol +++ b/contract/SimpleNftLowerGas.sol @@ -3,14 +3,16 @@ // Amended by HashLips /** !Disclaimer! + These contracts have been used to create tutorials, and was created for the purpose to teach people how to create smart contracts on the blockchain. please review this code on your own before using any of the following code for production. - HashLips will not be liable in any way for the use - of the code. That being said, the code has been tested - to the best of the developers' knowledge to work as intended. + The developer will not be responsible or liable for all loss or + damage whatsoever caused by you participating in any way in the + experimental code, whether putting money into the contract or + using the code for your own project. */ pragma solidity >=0.7.0 <0.9.0; @@ -162,4 +164,4 @@ contract SimpleNftLowerGas is ERC721, Ownable { function _baseURI() internal view virtual override returns (string memory) { return uriPrefix; } -} \ No newline at end of file +}