Skip to content

Instantly share code, notes, and snippets.

@zhouqiang-cl
Last active April 10, 2022 13:16
Show Gist options
  • Save zhouqiang-cl/c13a9753d579747b7d94bf2109160885 to your computer and use it in GitHub Desktop.
Save zhouqiang-cl/c13a9753d579747b7d94bf2109160885 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@test/ERC721A.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract Azuki is Ownable, ERC721A, ReentrancyGuard {
uint256 public immutable maxPerAddressDuringMint;
uint256 public immutable amountForDevs;
uint256 public immutable amountForAuctionAndDev;
struct SaleConfig {
uint32 auctionSaleStartTime;
uint32 publicSaleStartTime;
uint64 mintlistPrice;
uint64 publicPrice;
uint32 publicSaleKey;
}
SaleConfig public saleConfig;
mapping(address => uint256) public allowlist;
constructor(
uint256 maxBatchSize_,
uint256 collectionSize_,
uint256 amountForAuctionAndDev_,
uint256 amountForDevs_
) ERC721A("Azuki", "AZUKI", maxBatchSize_, collectionSize_) {
maxPerAddressDuringMint = maxBatchSize_;
amountForAuctionAndDev = amountForAuctionAndDev_;
amountForDevs = amountForDevs_;
require(
amountForAuctionAndDev_ <= collectionSize_,
"larger collection size needed"
);
}
modifier callerIsUser() {
require(tx.origin == msg.sender, "The caller is another contract");
_;
}
function auctionMint(uint256 quantity) external payable callerIsUser {
uint256 _saleStartTime = uint256(saleConfig.auctionSaleStartTime);
require(
_saleStartTime != 0 && block.timestamp >= _saleStartTime,
"sale has not started yet"
);
require(
totalSupply() + quantity <= amountForAuctionAndDev,
"not enough remaining reserved for auction to support desired mint amount"
);
require(
numberMinted(msg.sender) + quantity <= maxPerAddressDuringMint,
"can not mint this many"
);
uint256 totalCost = getAuctionPrice(_saleStartTime) * quantity;
_safeMint(msg.sender, quantity);
refundIfOver(totalCost);
}
function allowlistMint() external payable callerIsUser {
uint256 price = uint256(saleConfig.mintlistPrice);
require(price != 0, "allowlist sale has not begun yet");
require(allowlist[msg.sender] > 0, "not eligible for allowlist mint");
require(totalSupply() + 1 <= collectionSize, "reached max supply");
allowlist[msg.sender]--;
_safeMint(msg.sender, 1);
refundIfOver(price);
}
function publicSaleMint(uint256 quantity, uint256 callerPublicSaleKey)
external
payable
callerIsUser
{
SaleConfig memory config = saleConfig;
uint256 publicSaleKey = uint256(config.publicSaleKey);
uint256 publicPrice = uint256(config.publicPrice);
uint256 publicSaleStartTime = uint256(config.publicSaleStartTime);
require(
publicSaleKey == callerPublicSaleKey,
"called with incorrect public sale key"
);
require(
isPublicSaleOn(publicPrice, publicSaleKey, publicSaleStartTime),
"public sale has not begun yet"
);
require(totalSupply() + quantity <= collectionSize, "reached max supply");
require(
numberMinted(msg.sender) + quantity <= maxPerAddressDuringMint,
"can not mint this many"
);
_safeMint(msg.sender, quantity);
refundIfOver(publicPrice * quantity);
}
function refundIfOver(uint256 price) private {
require(msg.value >= price, "Need to send more ETH.");
if (msg.value > price) {
payable(msg.sender).transfer(msg.value - price);
}
}
function isPublicSaleOn(
uint256 publicPriceWei,
uint256 publicSaleKey,
uint256 publicSaleStartTime
) public view returns (bool) {
return
publicPriceWei != 0 &&
publicSaleKey != 0 &&
block.timestamp >= publicSaleStartTime;
}
uint256 public constant AUCTION_START_PRICE = 1 ether;
uint256 public constant AUCTION_END_PRICE = 0.15 ether;
uint256 public constant AUCTION_PRICE_CURVE_LENGTH = 340 minutes;
uint256 public constant AUCTION_DROP_INTERVAL = 20 minutes;
uint256 public constant AUCTION_DROP_PER_STEP =
(AUCTION_START_PRICE - AUCTION_END_PRICE) /
(AUCTION_PRICE_CURVE_LENGTH / AUCTION_DROP_INTERVAL);
function getAuctionPrice(uint256 _saleStartTime)
public
view
returns (uint256)
{
if (block.timestamp < _saleStartTime) {
return AUCTION_START_PRICE;
}
if (block.timestamp - _saleStartTime >= AUCTION_PRICE_CURVE_LENGTH) {
return AUCTION_END_PRICE;
} else {
uint256 steps = (block.timestamp - _saleStartTime) /
AUCTION_DROP_INTERVAL;
return AUCTION_START_PRICE - (steps * AUCTION_DROP_PER_STEP);
}
}
function endAuctionAndSetupNonAuctionSaleInfo(
uint64 mintlistPriceWei,
uint64 publicPriceWei,
uint32 publicSaleStartTime
) external onlyOwner {
saleConfig = SaleConfig(
0,
publicSaleStartTime,
mintlistPriceWei,
publicPriceWei,
saleConfig.publicSaleKey
);
}
function setAuctionSaleStartTime(uint32 timestamp) external onlyOwner {
saleConfig.auctionSaleStartTime = timestamp;
}
function setPublicSaleKey(uint32 key) external onlyOwner {
saleConfig.publicSaleKey = key;
}
function seedAllowlist(address[] memory addresses, uint256[] memory numSlots)
external
onlyOwner
{
require(
addresses.length == numSlots.length,
"addresses does not match numSlots length"
);
for (uint256 i = 0; i < addresses.length; i++) {
allowlist[addresses[i]] = numSlots[i];
}
}
// For marketing etc.
function devMint(uint256 quantity) external onlyOwner {
require(
totalSupply() + quantity <= amountForDevs,
"too many already minted before dev mint"
);
require(
quantity % maxBatchSize == 0,
"can only mint a multiple of the maxBatchSize"
);
uint256 numChunks = quantity / maxBatchSize;
for (uint256 i = 0; i < numChunks; i++) {
_safeMint(msg.sender, maxBatchSize);
}
}
// // metadata URI
string private _baseTokenURI;
function _baseURI() internal view virtual override returns (string memory) {
return _baseTokenURI;
}
function setBaseURI(string calldata baseURI) external onlyOwner {
_baseTokenURI = baseURI;
}
function withdrawMoney() external onlyOwner nonReentrant {
(bool success, ) = msg.sender.call{value: address(this).balance}("");
require(success, "Transfer failed.");
}
function setOwnersExplicit(uint256 quantity) external onlyOwner nonReentrant {
_setOwnersExplicit(quantity);
}
function numberMinted(address owner) public view returns (uint256) {
return _numberMinted(owner);
}
function getOwnershipData(uint256 tokenId)
external
view
returns (TokenOwnership memory)
{
return ownershipOf(tokenId);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment