Last active
December 7, 2021 20:29
-
-
Save freddiecabrera/0c9ca1ee72040d5797262ff9a0bb2e87 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=
This file contains 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
// contracts/Citizens.sol | |
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.4; | |
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
import "@openzeppelin/contracts/utils/Counters.sol"; | |
contract Citizens is ERC721, Ownable { | |
using Strings for uint256; | |
// Counter | |
using Counters for Counters.Counter; | |
Counters.Counter private _tokenSupply; | |
// Constant variables | |
// ------------------------------------------------------------------------ | |
uint256 public constant TOTAL_SUPPLY = 8800; // Total amount of Citizens | |
uint256 public constant RESERVED_SUPPLY = 300; // Amount of Citizens reserved for the contract | |
uint256 public constant MAX_SUPPLY = TOTAL_SUPPLY - RESERVED_SUPPLY; // Maximum amount of Citizens | |
uint256 public constant PRESALE_SUPPLY = 3000; // Presale supply | |
uint256 public constant MAX_PER_TX = 20; // Max amount of Citizens per tx (public sale) | |
uint256 public constant PRICE = 0.04 ether; | |
// Team addresses | |
// ------------------------------------------------------------------------ | |
address private constant _a1 = 0x62BDc056706570e1a93e432861f6AEbdf655d970; | |
address private constant _a2 = 0x533A3437720D83A00c42F9a9127525C0577bD9b7; | |
address private constant _a3 = 0xA582ad581f44Bf431f5f020242ab91a25170E200; | |
// State variables | |
// ------------------------------------------------------------------------ | |
bool public isPresaleActive = false; | |
bool public isPublicSaleActive = false; | |
bool public revealed = false; | |
// Presale arrays | |
// ------------------------------------------------------------------------ | |
mapping(address => bool) private _presaleEligible; | |
mapping(address => uint256) private _presaleClaimed; | |
// URI variables | |
// ------------------------------------------------------------------------ | |
string private _contractURI; | |
string public notRevealedURI; | |
string baseURI; | |
// Events | |
// ------------------------------------------------------------------------ | |
event BaseTokenURIChanged(string baseTokenURI); | |
event ContractURIChanged(string contractURI); | |
// Constructor | |
// ------------------------------------------------------------------------ | |
constructor(string memory _initBaseURI, string memory _initNotRevealedUri) | |
ERC721("ONE WRLD TEST", "CITIZEN ID") | |
{ | |
setBaseURI(_initBaseURI); | |
setNotRevealedURI(_initNotRevealedUri); | |
} | |
// Modifiers | |
// ------------------------------------------------------------------------ | |
modifier onlyPresale() { | |
require(isPresaleActive, "PRESALE_NOT_ACTIVE"); | |
_; | |
} | |
modifier onlyPublicSale() { | |
require(isPublicSaleActive, "PUBLIC_SALE_NOT_ACTIVE"); | |
_; | |
} | |
// Anti-bot functions | |
// ------------------------------------------------------------------------ | |
function isContractCall(address addr) internal view returns (bool) { | |
uint256 size; | |
assembly { | |
size := extcodesize(addr) | |
} | |
return size > 0; | |
} | |
// Presale functions | |
// ------------------------------------------------------------------------ | |
function addToPresaleList(address[] calldata addresses) external onlyOwner { | |
for (uint256 i = 0; i < addresses.length; i++) { | |
require(addresses[i] != address(0), "NULL_ADDRESS"); | |
require(!_presaleEligible[addresses[i]], "DUPLICATE_ENTRY"); | |
_presaleEligible[addresses[i]] = true; | |
_presaleClaimed[addresses[i]] = 0; | |
} | |
} | |
function removeFromPresaleList(address[] calldata addresses) | |
external | |
onlyOwner | |
{ | |
for (uint256 i = 0; i < addresses.length; i++) { | |
require(addresses[i] != address(0), "NULL_ADDRESS"); | |
require(_presaleEligible[addresses[i]], "NOT_IN_PRESALE"); | |
_presaleEligible[addresses[i]] = false; | |
} | |
} | |
function isEligibleForPresale(address addr) external view returns (bool) { | |
require(addr != address(0), "NULL_ADDRESS"); | |
return _presaleEligible[addr]; | |
} | |
function hasClaimedPresale(address addr) external view returns (bool) { | |
require(addr != address(0), "NULL_ADDRESS"); | |
return _presaleClaimed[addr] == 1; | |
} | |
function togglePresaleStatus() external onlyOwner { | |
isPresaleActive = !isPresaleActive; | |
} | |
function togglePublicSaleStatus() external onlyOwner { | |
isPublicSaleActive = !isPublicSaleActive; | |
} | |
// Mint functions | |
// ------------------------------------------------------------------------ | |
function claimReservedCitizen(uint256 quantity, address addr) | |
external | |
onlyOwner | |
{ | |
require(_tokenSupply.current() >= MAX_SUPPLY, "MUST_REACH_MAX_SUPPLY"); | |
require(_tokenSupply.current() < TOTAL_SUPPLY, "SOLD_OUT"); | |
require( | |
_tokenSupply.current() + quantity <= TOTAL_SUPPLY, | |
"EXCEEDS_TOTAL_SUPPLY" | |
); | |
for (uint256 i = 0; i < quantity; i++) { | |
_tokenSupply.increment(); | |
_safeMint(addr, _tokenSupply.current()); | |
} | |
} | |
function claimPresaleCitizen() external payable onlyPresale { | |
uint256 quantity = 1; | |
require(_presaleEligible[msg.sender], "NOT_ELIGIBLE_FOR_PRESALE"); | |
require(_presaleClaimed[msg.sender] < 1, "ALREADY_CLAIMED"); | |
require(_tokenSupply.current() < PRESALE_SUPPLY, "PRESALE_SOLD_OUT"); | |
require( | |
_tokenSupply.current() + quantity <= PRESALE_SUPPLY, | |
"EXCEEDS_PRESALE_SUPPLY" | |
); | |
if (msg.sender != owner()) { | |
require(PRICE * quantity == msg.value, "INVALID_ETH_AMOUNT"); | |
} | |
for (uint256 i = 0; i < quantity; i++) { | |
_presaleClaimed[msg.sender] += 1; | |
_tokenSupply.increment(); | |
_safeMint(msg.sender, _tokenSupply.current()); | |
} | |
} | |
function mint(uint256 quantity) external payable onlyPublicSale { | |
require(tx.origin == msg.sender, "GO_AWAY_BOT_ORIGIN"); | |
require(!isContractCall(msg.sender), "GO_AWAY_BOT_CONTRACT"); | |
require(_tokenSupply.current() < MAX_SUPPLY, "SOLD_OUT"); | |
require(quantity > 0, "QUANTITY_CANNOT_BE_ZERO"); | |
require(quantity <= MAX_PER_TX, "EXCEEDS_MAX_MINT"); | |
require( | |
_tokenSupply.current() + quantity <= MAX_SUPPLY, | |
"EXCEEDS_MAX_SUPPLY" | |
); | |
if (msg.sender != owner()) { | |
require(PRICE * quantity == msg.value, "INVALID_ETH_AMOUNT"); | |
} | |
for (uint256 i = 0; i < quantity; i++) { | |
_tokenSupply.increment(); | |
_safeMint(msg.sender, _tokenSupply.current()); | |
} | |
} | |
function tokensMinted() public view returns (uint256) { | |
return _tokenSupply.current(); | |
} | |
// Base URI Functions | |
// ------------------------------------------------------------------------ | |
function setContractURI(string calldata URI) external onlyOwner { | |
_contractURI = URI; | |
emit ContractURIChanged(URI); | |
} | |
function contractURI() public view returns (string memory) { | |
return _contractURI; | |
} | |
function _baseURI() internal view override returns (string memory) { | |
return baseURI; | |
} | |
function tokenURI(uint256 tokenId) | |
public | |
view | |
virtual | |
override | |
returns (string memory) | |
{ | |
require( | |
_exists(tokenId), | |
"ERC721Metadata: URI query for nonexistent token" | |
); | |
if (revealed == false) { | |
return notRevealedURI; | |
} | |
string memory currentBaseURI = _baseURI(); | |
return | |
bytes(currentBaseURI).length > 0 | |
? string( | |
abi.encodePacked( | |
currentBaseURI, | |
tokenId.toString(), | |
".json" | |
) | |
) | |
: ""; | |
} | |
//only owner | |
function reveal() public onlyOwner { | |
revealed = true; | |
} | |
function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner { | |
notRevealedURI = _notRevealedURI; | |
} | |
function setBaseURI(string memory _newBaseURI) public onlyOwner { | |
baseURI = _newBaseURI; | |
} | |
// Withdrawal functions | |
// ------------------------------------------------------------------------ | |
function withdrawAll() external onlyOwner { | |
uint256 _a1amount = (address(this).balance * 20) / 100; | |
uint256 _a2amount = (address(this).balance * 40) / 100; | |
uint256 _a3amount = (address(this).balance * 40) / 100; | |
require(payable(_a1).send(_a1amount), "FAILED_TO_SEND_TO_A1"); | |
require(payable(_a2).send(_a2amount), "FAILED_TO_SEND_TO_A2"); | |
require(payable(_a3).send(_a3amount), "FAILED_TO_SEND_TO_A3"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@cybergirldinah, @parseb, and @maximepeabody thank you all so much for taking the time to review the contract. I made the updates suggested 😃