Skip to content

Commit ddcde39

Browse files
committed
Add finish raffle
1 parent c6b58e5 commit ddcde39

File tree

6 files changed

+303
-9
lines changed

6 files changed

+303
-9
lines changed

contracts/RaffleFactory.sol

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ contract RaffleFactory is Initializable, UUPSUpgradeable, AccessControlUpgradeab
1212
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
1313
address[] public raffles;
1414

15-
event RaffleCreated(address indexed raffleAddress, address indexed creator);
15+
event RaffleCreated(uint256 indexed raffleId, address indexed raffleAddress, address indexed creator);
1616

1717
/// @custom:oz-upgrades-unsafe-allow constructor
1818
constructor() {
@@ -38,7 +38,7 @@ contract RaffleFactory is Initializable, UUPSUpgradeable, AccessControlUpgradeab
3838
_;
3939
}
4040

41-
// Reentrancy guarded via `nonReentrant` modifier
41+
// Reentrancy is guarded via the `nonReentrant` modifier
4242
// slither-disable-next-line reentrancy-benign
4343
function createRaffle(
4444
string memory name_,
@@ -61,8 +61,18 @@ contract RaffleFactory is Initializable, UUPSUpgradeable, AccessControlUpgradeab
6161
"Approve to raffle failed"
6262
);
6363
// Start the raffle (transfer tokens to the contract)
64-
raffle.startRaffle(prizeToken, amount);
65-
emit RaffleCreated(address(raffle), msg.sender);
64+
raffle.start(prizeToken, amount);
65+
uint256 raffleId = raffles.length - 1;
66+
emit RaffleCreated(raffleId, address(raffle), msg.sender);
6667
return address(raffle);
6768
}
69+
70+
function finishRaffle(uint256 raffleId) external onlyManager nonReentrant {
71+
require(raffleId < raffles.length, "Raffle does not exist");
72+
address raffleAddr = raffles[raffleId];
73+
RaffleNFT raffle = RaffleNFT(raffleAddr);
74+
require(raffle.winnerAddress() == address(0), "Winner already determined");
75+
require(raffle.started(), "Raffle not started");
76+
raffle.finish();
77+
}
6878
}

contracts/RaffleNFT.sol

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,34 @@ pragma solidity ^0.8.28;
44
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
55
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
66
import "@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
}

hardhat.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require("@nomicfoundation/hardhat-toolbox");
22
require('@openzeppelin/hardhat-upgrades');
3+
require("dotenv").config();
34

45
/** @type import('hardhat/config').HardhatUserConfig */
56
module.exports = {
@@ -12,4 +13,17 @@ module.exports = {
1213
},
1314
},
1415
},
16+
networks: {
17+
hardhat: {
18+
forking: {
19+
url: "https://rpc.eth.haqq.network",
20+
},
21+
},
22+
'haqq-testedge2': {
23+
url: `https://rpc.eth.testedge2.haqq.network`,
24+
},
25+
haqq: {
26+
url: "https://rpc.eth.haqq.network",
27+
},
28+
},
1529
};

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@nomicfoundation/hardhat-toolbox": "^6.0.0",
1414
"@openzeppelin/contracts-upgradeable": "^5.3.0",
1515
"@openzeppelin/hardhat-upgrades": "^3.9.1",
16+
"dotenv": "^17.2.0",
1617
"hardhat": "^2.25.0"
1718
}
1819
}

0 commit comments

Comments
 (0)