forked from Vectorized/dn404
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: AllowlistMintDN404 (Vectorized#37)
* feat: SimpleAllowlistDN404 * chore: live * Rename and add total supply --------- Co-authored-by: pop-punk <[email protected]> Co-authored-by: cygaar <[email protected]>
- Loading branch information
1 parent
30f4cee
commit ba193e4
Showing
5 changed files
with
231 additions
and
1 deletion.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// SPX-License-Identifier: MIT | ||
pragma solidity ^0.8.4; | ||
|
||
import "../DN404.sol"; | ||
import "../DN404Mirror.sol"; | ||
import {Ownable} from "solady/auth/Ownable.sol"; | ||
import {LibString} from "solady/utils/LibString.sol"; | ||
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; | ||
import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol"; | ||
|
||
contract AllowlistMintDN404 is DN404, Ownable { | ||
string private _name; | ||
string private _symbol; | ||
string private _baseURI; | ||
bytes32 private allowlistRoot; | ||
uint120 public publicPrice; | ||
uint120 public allowlistPrice; | ||
bool public live; | ||
uint256 public maxMint; | ||
uint256 public numMinted; | ||
|
||
uint256 public constant MAX_SUPPLY = 5000; | ||
|
||
mapping(address => bool) public minted; | ||
|
||
error InvalidProof(); | ||
error InvalidMint(); | ||
error InvalidPrice(); | ||
error ExceedsMaxMint(); | ||
error TotalSupplyReached(); | ||
error NotLive(); | ||
|
||
modifier isValidMint(uint256 price, uint256 amount) { | ||
if (!live) { | ||
revert NotLive(); | ||
} | ||
if (price * amount != msg.value) { | ||
revert InvalidPrice(); | ||
} | ||
if (amount > maxMint) { | ||
revert ExceedsMaxMint(); | ||
} | ||
if (numMinted + amount > MAX_SUPPLY) { | ||
revert TotalSupplyReached(); | ||
} | ||
_; | ||
} | ||
|
||
constructor( | ||
string memory name_, | ||
string memory symbol_, | ||
bytes32 allowlistRoot_, | ||
uint256 maxMint_, | ||
uint120 publicPrice_, | ||
uint120 allowlistPrice_, | ||
uint96 initialTokenSupply, | ||
address initialSupplyOwner | ||
) { | ||
_initializeOwner(msg.sender); | ||
|
||
_name = name_; | ||
_symbol = symbol_; | ||
allowlistRoot = allowlistRoot_; | ||
maxMint = maxMint_; | ||
publicPrice = publicPrice_; | ||
allowlistPrice = allowlistPrice_; | ||
|
||
address mirror = address(new DN404Mirror(msg.sender)); | ||
_initializeDN404(initialTokenSupply, initialSupplyOwner, mirror); | ||
} | ||
|
||
function mint(uint256 amount) public payable isValidMint(publicPrice, amount) { | ||
if (minted[msg.sender]) revert InvalidMint(); | ||
minted[msg.sender] = true; | ||
unchecked { | ||
++numMinted; | ||
} | ||
_mint(msg.sender, amount); | ||
} | ||
|
||
function allowlistMint(uint256 amount, bytes32[] calldata proof) | ||
public | ||
payable | ||
isValidMint(allowlistPrice, amount) | ||
{ | ||
if ( | ||
!MerkleProofLib.verifyCalldata( | ||
proof, allowlistRoot, keccak256(abi.encodePacked(msg.sender)) | ||
) | ||
) { | ||
revert InvalidProof(); | ||
} | ||
if (minted[msg.sender]) revert InvalidMint(); | ||
minted[msg.sender] = true; | ||
unchecked { | ||
++numMinted; | ||
} | ||
_mint(msg.sender, amount); | ||
} | ||
|
||
function setBaseURI(string calldata baseURI_) public onlyOwner { | ||
_baseURI = baseURI_; | ||
} | ||
|
||
function setPrices(uint120 publicPrice_, uint120 allowlistPrice_) public onlyOwner { | ||
publicPrice = publicPrice_; | ||
allowlistPrice = allowlistPrice_; | ||
} | ||
|
||
function setMaxMint(uint256 maxMint_) public onlyOwner { | ||
maxMint = maxMint_; | ||
} | ||
|
||
function toggleLive() public onlyOwner { | ||
live = !live; | ||
} | ||
|
||
function withdraw() public onlyOwner { | ||
SafeTransferLib.safeTransferAllETH(msg.sender); | ||
} | ||
|
||
function name() public view override returns (string memory) { | ||
return _name; | ||
} | ||
|
||
function symbol() public view override returns (string memory) { | ||
return _symbol; | ||
} | ||
|
||
function tokenURI(uint256 tokenId) public view override returns (string memory result) { | ||
if (bytes(_baseURI).length != 0) { | ||
result = string(abi.encodePacked(_baseURI, LibString.toString(tokenId))); | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.4; | ||
|
||
import "./utils/SoladyTest.sol"; | ||
import {AllowlistMintDN404} from "../src/example/AllowlistMintDN404.sol"; | ||
import {Merkle} from "murky/src/Merkle.sol"; | ||
|
||
contract AllowlistMintDN404Test is SoladyTest { | ||
AllowlistMintDN404 dn; | ||
Merkle allowlistMerkle; | ||
|
||
address alice = address(111); | ||
address bob = address(222); | ||
|
||
bytes32 allowlistRoot; | ||
bytes32[] allowlistData = new bytes32[](2); | ||
|
||
uint120 publicPrice = 0.02 ether; | ||
uint120 allowlistPrice = 0.01 ether; | ||
|
||
function setUp() public { | ||
allowlistMerkle = new Merkle(); | ||
allowlistData[0] = bytes32(keccak256(abi.encodePacked(alice))); | ||
allowlistRoot = allowlistMerkle.getRoot(allowlistData); | ||
|
||
dn = new AllowlistMintDN404( | ||
"DN404", "DN", allowlistRoot, 10, publicPrice, allowlistPrice, 1000, address(this) | ||
); | ||
dn.toggleLive(); | ||
payable(bob).transfer(10 ether); | ||
payable(alice).transfer(10 ether); | ||
} | ||
|
||
function testMint() public { | ||
vm.startPrank(bob); | ||
|
||
vm.expectRevert(AllowlistMintDN404.InvalidPrice.selector); | ||
dn.mint{value: 1 ether}(1); | ||
|
||
vm.expectRevert(AllowlistMintDN404.ExceedsMaxMint.selector); | ||
dn.mint{value: 11 * publicPrice}(11); | ||
|
||
dn.mint{value: 5 * publicPrice}(5); | ||
assertEq(dn.totalSupply(), 1005); | ||
assertEq(dn.balanceOf(bob), 5); | ||
|
||
vm.expectRevert(AllowlistMintDN404.InvalidMint.selector); | ||
dn.mint{value: 6 * publicPrice}(6); | ||
|
||
vm.stopPrank(); | ||
} | ||
|
||
function testTotalSupplyReached() public { | ||
// Mint out whole supply | ||
for (uint160 i; i < 5000; ++i) { | ||
address a = address(i + 1000); | ||
payable(a).transfer(1 ether); | ||
vm.prank(a); | ||
dn.mint{value: publicPrice}(1); | ||
} | ||
|
||
vm.prank(alice); | ||
vm.expectRevert(AllowlistMintDN404.TotalSupplyReached.selector); | ||
dn.mint{value: publicPrice}(1); | ||
} | ||
|
||
function testAllowlistMint() public { | ||
vm.prank(bob); | ||
|
||
bytes32[] memory proof = allowlistMerkle.getProof(allowlistData, 0); | ||
vm.expectRevert(AllowlistMintDN404.InvalidProof.selector); | ||
dn.allowlistMint{value: 5 * allowlistPrice}(5, proof); | ||
|
||
vm.startPrank(alice); | ||
|
||
vm.expectRevert(AllowlistMintDN404.InvalidPrice.selector); | ||
dn.allowlistMint{value: 1 ether}(1, proof); | ||
|
||
vm.expectRevert(AllowlistMintDN404.ExceedsMaxMint.selector); | ||
dn.allowlistMint{value: 11 * allowlistPrice}(11, proof); | ||
|
||
dn.allowlistMint{value: 5 * allowlistPrice}(5, proof); | ||
assertEq(dn.totalSupply(), 1005); | ||
assertEq(dn.balanceOf(alice), 5); | ||
|
||
vm.expectRevert(AllowlistMintDN404.InvalidMint.selector); | ||
dn.allowlistMint{value: 6 * allowlistPrice}(6, proof); | ||
|
||
vm.stopPrank(); | ||
} | ||
} |