Created
December 4, 2021 02:12
-
-
Save jt-metatheory/c49452664f1f33fa54cf2d2e35732f7e to your computer and use it in GitHub Desktop.
DuskBreakers Contract
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: 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