-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAdvancedNFT.sol
More file actions
155 lines (117 loc) · 4.79 KB
/
Copy pathAdvancedNFT.sol
File metadata and controls
155 lines (117 loc) · 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Import OpenZeppelin Libraries
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import "@openzeppelin/contracts/utils/Multicall.sol";
contract SiriAdvanced is ERC721, Ownable, Multicall {
using BitMaps for BitMaps.BitMap;
// --- Sale State Machine ---
enum SaleState {
CLOSED,
PRESALE,
PUBLIC,
SOLD_OUT
}
SaleState public saleState;
// --- Merkle Airdrop ---
bytes32 public merkleRoot;
BitMaps.BitMap private claimedBitmap;
// Mapping way for comparison
mapping(address => bool) public hasMintedMapping;
// --- Commit Reveal Randomness ---
struct Commitment {
uint256 blockNumber;
bytes32 commitHash;
}
mapping(address => Commitment) public commitments;
uint256 public totalSupply;
uint256 public maxSupply;
// --- Pull Payment Withdrawal ---
mapping(address => uint256) public pendingWithdrawals;
// --- Constructor ---
constructor(uint256 _maxSupply, bytes32 _merkleRoot) ERC721("SiriAdvanced", "SIRI") Ownable(msg.sender) {
maxSupply = _maxSupply;
merkleRoot = _merkleRoot;
saleState = SaleState.CLOSED;
}
// --- Merkle Mint ---
function merkleMint(uint256 index, bytes32[] calldata _proof) external {
require(saleState == SaleState.PRESALE, "Presale not active");
require(totalSupply < maxSupply, "Sold out");
// Calculate leaf
bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
// Verify Merkle Proof
require(MerkleProof.verify(_proof, merkleRoot, leaf), "Invalid proof");
// Check bitmap or mapping
require(!claimedBitmap.get(index), "Already claimed");
// Mark as claimed in bitmap
claimedBitmap.set(index);
// Track with mapping also (for gas comparison)
require(!hasMintedMapping[msg.sender], "Already minted");
hasMintedMapping[msg.sender] = true;
// Commit Phase (commit a random secret)
// You should call `commit` separately (see below)
// Minting part
_safeMint(msg.sender, totalSupply);
totalSupply++;
if (totalSupply >= maxSupply) {
saleState = SaleState.SOLD_OUT;
}
}
//PublicSale -- Public Mint
function publicMint() external {
require(saleState == SaleState.PUBLIC, "Public sale not active");
require(totalSupply < maxSupply, "Sold out");
_safeMint(msg.sender, totalSupply);
totalSupply++;
if (totalSupply >= maxSupply) {
saleState = SaleState.SOLD_OUT;
}
}
// --- Commit Reveal Randomness ---
function commit(bytes32 commitHash) external {
commitments[msg.sender] = Commitment(block.number, commitHash);
}
function reveal(uint256 secret, string calldata salt) external {
Commitment memory userCommit = commitments[msg.sender];
require(userCommit.blockNumber > 0, "No commitment found");
require(block.number > userCommit.blockNumber + 10, "Reveal not ready yet");
// Verify commit matches
bytes32 expectedCommitHash = keccak256(abi.encodePacked(secret, salt));
require(userCommit.commitHash == expectedCommitHash, "Commitment mismatch");
// Generate Random NFT ID
uint256 randomNFTId = uint256(keccak256(abi.encodePacked(secret, blockhash(userCommit.blockNumber + 10)))) % maxSupply;
// Minting Random NFT
_safeMint(msg.sender, randomNFTId);
delete commitments[msg.sender];
}
// --- Admin functions ---
function setSaleState(SaleState _state) external onlyOwner {
saleState = _state;
}
function setMerkleRoot(bytes32 _root) external onlyOwner {
merkleRoot = _root;
}
// --- Pull Payments ---
function withdraw() external {
uint256 amount = pendingWithdrawals[msg.sender];
require(amount > 0, "Nothing to withdraw");
pendingWithdrawals[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}
function depositShares(address[] calldata contributors, uint256[] calldata amounts) external payable onlyOwner {
require(contributors.length == amounts.length, "Length mismatch");
uint256 total;
for (uint i = 0; i < amounts.length; i++) {
total += amounts[i];
pendingWithdrawals[contributors[i]] += amounts[i];
}
require(total <= msg.value, "Insufficient deposit");
}
// --- Multicall (already inherited) ---
// Note: Inherited from OpenZeppelin's Multicall.sol
// It lets you batch multiple calls in a single transaction!
}