Skip to content

Instantly share code, notes, and snippets.

@jt-metatheory
Created December 4, 2021 02:12
Show Gist options
  • Save jt-metatheory/c49452664f1f33fa54cf2d2e35732f7e to your computer and use it in GitHub Desktop.
Save jt-metatheory/c49452664f1f33fa54cf2d2e35732f7e to your computer and use it in GitHub Desktop.
DuskBreakers Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract DuskBreakers is ERC721Enumerable, Ownable, ReentrancyGuard {
using Strings for string;
using SafeMath for uint256;
using Counters for Counters.Counter;
/*
* Enforce the existence of only 10 Breakers.
*/
uint256 public constant BREAKER_SUPPLY = 10000;
uint256 public constant breakerPrice = 0.06 ether;
string public baseTokenURI;
bool private PAUSE = true;
bool private fcfsMint = false;
// Ensure Fairness
string public BREAKER_PROVENANCE = "";
uint256 public startingIndexBlock;
uint256 public startingIndex;
// tokenID storage
Counters.Counter private _tokenIdCounter;
// Presale, Play2Mint
PresaleConfig public presaleConfig;
Play2MintConfig public play2MintConfig;
// Presales and Play2Mints mappings
mapping(address => uint256) private _presaleList;
mapping(address => uint256) private _play2MintList;
// Presale and Play2Mint Structs
struct PresaleConfig {
uint256 mintMax;
}
struct Play2MintConfig {
uint256 mintMax;
}
// Breaker Contract Events
event ChangePresaleConfig(
uint256 _mintMax
);
event ChangePlay2MintConfig(
uint256 _mintMax
);
// Events
event PauseEvent(bool pause);
//event welcomeToTheDusk(uint256 indexed id);
// Safety Modifiers
function isUnpausedAndSupplied() public view returns (bool){
return (hasSupply(1)) && (!PAUSE);
}
modifier saleIsOpen {
require(hasSupply(1), "Breakers Sold Out!");
require(!PAUSE, "Sales not open");
_;
}
function setPause(bool _pause) public onlyOwner{
PAUSE = _pause;
emit PauseEvent(PAUSE);
}
// Open Floodgates for FCFS
event FCFSEvent(bool pause);
function setFCFS(bool _fcfsMint) public onlyOwner{
fcfsMint = _fcfsMint;
emit FCFSEvent(fcfsMint);
}
// Token URI overrides
function _baseURI() internal view virtual override returns (string memory) {
return baseTokenURI;
}
function setBaseURI(string memory baseURI) public onlyOwner {
baseTokenURI = baseURI;
}
// token counters
function nextToken() public view returns (uint256) {
return _tokenIdCounter.current();
}
constructor(string memory name, string memory symbol) ERC721(name, symbol){
// First we allow presales to mint during Play2Mint
// Then, we allow Play2Mint 1 day
// Finally we open for anyone to mint
//preseale starts on contract creation
uint256 _mintMax = 2;
presaleConfig = PresaleConfig(_mintMax);
emit ChangePresaleConfig(_mintMax);
// Play2Mint allow minting config
uint256 _p2MmintMax = 3;
play2MintConfig = Play2MintConfig(_p2MmintMax);
emit ChangePlay2MintConfig(_p2MmintMax);
setBaseURI("https://duskbreakers.gg/api/breakers/");
}
/*
* Fairness Section....
* Pre mint, we set the provenance hash to the hash of hashes of the sha256
* in order of original image generation
* We then have an offest based on the last block sold, modulo the total supply
* Thus you know we genereated them in the order we said and anyone including us had no way
* to know what the starting index will be
*/
function setProvenanceHash(string memory provenanceHash) public onlyOwner {
BREAKER_PROVENANCE = provenanceHash;
}
// It has gone wrong and the presale didn't fully mint so this allows us to reveal
function emergencySetStartingIndexBlock() public onlyOwner {
require(startingIndex == 0, "Starting index is already set");
startingIndexBlock = block.number;
}
function setStartingIndex() public {
require(startingIndex == 0, "Starting index is already set");
require(startingIndexBlock != 0, "Starting index block must be set");
startingIndex = uint(blockhash(startingIndexBlock)) % BREAKER_SUPPLY;
// Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
if (block.number.sub(startingIndexBlock) > 255) {
startingIndex = uint(blockhash(block.number - 1)) % BREAKER_SUPPLY;
}
// Prevent default sequence
if (startingIndex == 0) {
startingIndex = startingIndex.add(1);
}
}
//
function addTreasury(address _address)
external
onlyOwner
{
require(
_address != address(0),
"Breaker Treasury: Can't add a zero address"
);
if (_presaleList[_address] == 0) {
_presaleList[_address] = 420; // It seems appropriate
}
}
// Presale and Play2Mint whitelists
function addToPresaleList(address[] calldata _addresses)
external
onlyOwner
{
for (uint256 ind = 0; ind < _addresses.length; ind++) {
require(
_addresses[ind] != address(0),
"Breaker Presale: Can't add a zero address"
);
if (_presaleList[_addresses[ind]] == 0) {
_presaleList[_addresses[ind]] = presaleConfig.mintMax;
}
}
}
function addToPlay2Mint(address[] calldata _addresses)
external
onlyOwner
{
for (uint256 ind = 0; ind < _addresses.length; ind++) {
require(
_addresses[ind] != address(0),
"DuskBreaker P2M: Can't add a zero address"
);
if (_play2MintList[_addresses[ind]] == 0) {
_play2MintList[_addresses[ind]] = play2MintConfig.mintMax;
}
}
}
function mintBreaker(uint256 numberOfTokens) public payable saleIsOpen nonReentrant() {
require(hasSupply(numberOfTokens), "Purchase exceeds max total Breakers");
require(canMint(numberOfTokens), "Mint Access Not Granted");
require(breakerPrice.mul(numberOfTokens) <= msg.value, "ETH sent in transaction too low");
for(uint i = 0; i < numberOfTokens; i++) {
uint mintIndex = nextToken();
if (mintIndex < BREAKER_SUPPLY) {
_safeMint(msg.sender, mintIndex);
//emit welcomeToTheDusk(mintIndex);
_tokenIdCounter.increment();
// remove presales sales first then play2mint sales next
if (_presaleList[msg.sender] > 0) {
_presaleList[msg.sender] = _presaleList[msg.sender].sub(1);
} else if (_play2MintList[msg.sender] > 0) {
_play2MintList[msg.sender] = _play2MintList[msg.sender].sub(1);
}
}
}
// Did we mint the last block? If so, ready the starting index based on this block number
if (startingIndexBlock == 0 && (nextToken() == BREAKER_SUPPLY)) {
startingIndexBlock = block.number;
}
}
// Safeguards
function hasSupply(uint256 numberOfTokens) public view returns (bool) {
uint256 creatureSupply = nextToken();
return creatureSupply.add(numberOfTokens) <= BREAKER_SUPPLY;
}
function canMint(uint256 numberOfTokens) public view returns (bool) {
// skip guard so web3 has easy time
if(! isUnpausedAndSupplied()) {
return false;
}
// Presales can mint their max
if(_presaleList[msg.sender] > 0 &&
numberOfTokens <= _presaleList[msg.sender]
) {
return true;
}
// same for play2mint people, but if you are both whitelist and p2m, you can mint the sum
if(_play2MintList[msg.sender] > 0 &&
numberOfTokens <= _play2MintList[msg.sender]
) {
return true;
}
// after Play2Mint, anyone can mint
if(fcfsMint) {
return true;
}
return false;
}
// Web3 Economics
function withdrawAll() public onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0);
_widthdraw(msg.sender, address(this).balance);
}
function _widthdraw(address _address, uint256 _amount) private {
(bool success, ) = _address.call{value: _amount}("");
require(success, "Transfer failed.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment