@@ -4,26 +4,34 @@ pragma solidity ^0.8.28;
44import "@openzeppelin/contracts/token/ERC721/ERC721.sol " ;
55import "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
66import "@openzeppelin/contracts/utils/ReentrancyGuard.sol " ;
7+ import "@openzeppelin/contracts/access/Ownable.sol " ;
78
8- contract RaffleNFT is ERC721 , ReentrancyGuard {
9+ contract RaffleNFT is ERC721 , ReentrancyGuard , Ownable {
910 address public prizeToken;
1011 uint256 public amount;
1112 bool public started;
1213 string private baseTokenURI;
14+ uint256 private _tokenIdCounter;
15+ mapping (uint256 => address ) public tokenOwners;
16+ mapping (address => uint256 ) public lastMintTime;
17+ address public winnerAddress;
18+
19+ event Start (address indexed prizeToken , uint256 amount );
20+ event Finish (address indexed prizeToken , uint256 amount , address indexed winner );
1321
1422 constructor (
1523 string memory name_ ,
1624 string memory symbol_ ,
1725 string memory tokenURI_
18- ) ERC721 (name_, symbol_) {
26+ ) ERC721 (name_, symbol_) Ownable ( msg . sender ) {
1927 baseTokenURI = tokenURI_;
2028 started = false ;
2129 }
2230
23- function startRaffle (
31+ function start (
2432 address prizeToken_ ,
2533 uint256 amount_
26- ) external nonReentrant {
34+ ) external onlyOwner nonReentrant {
2735 require (prizeToken_ != address (0 ), "Zero address " );
2836 require (! started, "Already started " );
2937 started = true ;
@@ -33,9 +41,65 @@ contract RaffleNFT is ERC721, ReentrancyGuard {
3341 IERC20 (prizeToken).transferFrom (msg .sender , address (this ), amount),
3442 "ERC20 transfer failed "
3543 );
44+ emit Start (prizeToken_, amount_);
45+ }
46+
47+ function finish () external onlyOwner nonReentrant {
48+ require (started, "Raffle not started " );
49+ require (_tokenIdCounter > 0 , "No participants " );
50+ require (amount > 0 , "No prize " );
51+
52+ uint256 winnerTokenId = uint256 (
53+ keccak256 (abi.encodePacked (
54+ block .prevrandao ,
55+ address (this ),
56+ tx .gasprice ,
57+ gasleft (),
58+ _tokenIdCounter
59+ ))
60+ ) % _tokenIdCounter;
61+ address winner = ownerOf (winnerTokenId);
62+ require (winner != address (0 ), "Winner not found " );
63+ winnerAddress = winner;
64+
65+ uint256 prize = amount;
66+ amount = 0 ;
67+ started = false ;
68+ require (
69+ IERC20 (prizeToken).transfer (winner, prize),
70+ "ERC20 transfer to winner failed "
71+ );
72+ emit Finish (prizeToken, prize, winner);
73+ }
74+
75+ function mint () public {
76+ // We intentionally use block.timestamp to limit NFT minting to once every 24 hours per address.
77+ // This is acceptable for our use case, as precise cryptographic unpredictability is not required here.
78+ // slither-disable-next-line timestamp
79+ require (
80+ block .timestamp - lastMintTime[msg .sender ] >= 1 days,
81+ "You can only mint once every 24 hours "
82+ );
83+ uint256 tokenId = _tokenIdCounter;
84+ _mint (msg .sender , tokenId);
85+ tokenOwners[tokenId] = msg .sender ;
86+ _tokenIdCounter++ ;
87+ lastMintTime[msg .sender ] = block .timestamp ;
3688 }
3789
3890 function tokenURI (uint256 ) public view override returns (string memory ) {
3991 return baseTokenURI;
4092 }
93+
94+ function updateTokenURI (string memory tokenURI_ ) external onlyOwner {
95+ baseTokenURI = tokenURI_;
96+ }
97+
98+ function _update (address to , uint256 tokenId , address auth ) internal override returns (address ) {
99+ address from = super ._update (to, tokenId, auth);
100+ if (from != to && to != address (0 )) {
101+ tokenOwners[tokenId] = to;
102+ }
103+ return from;
104+ }
41105}
0 commit comments