Skip to content

Instantly share code, notes, and snippets.

@maximedegreve
Last active January 30, 2023 16:45
Show Gist options
  • Save maximedegreve/c9bbe894388b2c5203ee0470e67207b6 to your computer and use it in GitHub Desktop.
Save maximedegreve/c9bbe894388b2c5203ee0470e67207b6 to your computer and use it in GitHub Desktop.
TinyFaces NFT Lottery 1.1 (Contract)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import 'erc721a/contracts/ERC721A.sol';
import 'erc721a-upgradeable/contracts/ERC721AUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "operator-filter-registry/src/upgradeable/OperatorFiltererUpgradeable.sol";
import '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';
error LotteryNotActive();
error ExceededLimit();
error WrongEther();
error InvalidMerkle();
contract TinyFacesCelebs is ERC721AUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable, OperatorFiltererUpgradeable {
using AddressUpgradeable for address;
using StringsUpgradeable for uint256;
using MerkleProof for bytes32[];
address proxyRegistryAddress;
address payable[] private participants;
address public originalAddress;
bytes32 public merkleRoot;
uint256 public ticketRate;
string public baseExtension;
string public baseURI;
bool public lotteryActive;
function initialize() initializerERC721A initializer public {
__ERC721A_init('TinyFaces Celebrities', 'TINY-CELEBS');
__Ownable_init();
__ReentrancyGuard_init();
OperatorFiltererUpgradeable.__OperatorFilterer_init(address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6), true);
ticketRate = 0.00 ether;
baseExtension = '.json';
baseURI = '';
lotteryActive = false;
originalAddress = address(0);
}
function pickWinner(string memory _newBaseURI) external onlyOwner {
if (!lotteryActive) revert LotteryNotActive();
address winner;
uint index = random() % participants.length;
winner = participants[index];
baseURI = _newBaseURI;
lotteryActive = false;
delete participants;
_mint(winner, 1);
}
function buyTicket(uint256 quantity, bytes32[] calldata proof) external payable nonReentrant {
if (!lotteryActive) revert LotteryNotActive();
if (!isWhiteListed(msg.sender, proof)) revert InvalidMerkle();
if (ticketRate * quantity != msg.value) {
revert WrongEther();
}
address payable sender = payable(msg.sender);
uint256 left = ticketsLeft(msg.sender);
if(quantity > left){
revert ExceededLimit();
}
for(uint256 i = 0; i < quantity; i++) {
participants.push(sender);
}
}
function ticketsLeft(address _account) public view returns (uint256) {
ERC721A originalTinyFaces = ERC721A(originalAddress);
uint256 balance = originalTinyFaces.balanceOf(_account);
address payable sender = payable(_account);
uint256 numTickets = ownedTickets(sender);
return balance-numTickets;
}
function ownedTickets(address _account) public view returns (uint256) {
uint256 numTickets = 0;
address payable sender = payable(_account);
for(uint256 i = 0; i < participants.length; i++) {
if(participants[i] == sender){
numTickets++;
}
}
return numTickets;
}
function totalTickets() public view returns (uint256) {
return participants.length;
}
function isWhiteListed(address _account, bytes32[] calldata _proof) public view returns (bool) {
return _verify(leaf(_account), _proof);
}
function leaf(address _account) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(_account));
}
function _verify(bytes32 _leaf, bytes32[] memory _proof) internal view returns (bool) {
return MerkleProof.verify(_proof, merkleRoot, _leaf);
}
function setMerkleRoot(bytes32 _root) external onlyOwner {
merkleRoot = _root;
}
function withdraw() external payable onlyOwner {
payable(owner()).transfer(address(this).balance);
}
function tokenURI(uint256 tokenId) public view 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))
: '';
}
function _baseURI() internal view override returns (string memory) {
return baseURI;
}
function setBaseURI(string memory _newBaseURI) public onlyOwner {
baseURI = _newBaseURI;
}
function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
baseExtension = _newBaseExtension;
}
function toggleLottery() public onlyOwner {
lotteryActive = !lotteryActive;
}
function renounceOwnership() public override onlyOwner {}
function setTicketRate(uint256 _ticketRate) public onlyOwner {
ticketRate = _ticketRate;
}
function setOriginalAddress(address _address) public onlyOwner {
originalAddress = _address;
}
function random() onlyOwner public view returns(uint){
return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, participants.length)));
}
function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
super.setApprovalForAll(operator, approved);
}
function approve(address operator, uint256 tokenId) public payable override onlyAllowedOperatorApproval(operator) {
super.approve(operator, tokenId);
}
function transferFrom(address from, address to, uint256 tokenId) public payable override onlyAllowedOperator(from) {
super.transferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public payable override onlyAllowedOperator(from) {
super.safeTransferFrom(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
public
payable
override
onlyAllowedOperator(from)
{
super.safeTransferFrom(from, to, tokenId, data);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment