From 9e54c73fe5519dca5b2e921db4a394e8ac1f06fb Mon Sep 17 00:00:00 2001 From: Jeffrey Scholz Date: Thu, 27 Apr 2023 17:40:04 +0700 Subject: [PATCH] add furry fox friends --- contracts/FurryFoxFriends.sol | 67 +++++++++++++++++++++++++++++++++++ test/FurryFoxFriends.js | 35 ++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 contracts/FurryFoxFriends.sol create mode 100644 test/FurryFoxFriends.js diff --git a/contracts/FurryFoxFriends.sol b/contracts/FurryFoxFriends.sol new file mode 100644 index 0000000..5966d03 --- /dev/null +++ b/contracts/FurryFoxFriends.sol @@ -0,0 +1,67 @@ +//SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/access/Ownable2Step.sol"; + +/* + +$$$$$$$$\ $$$$$$$$\ +$$ _____| $$ _____| +$$ | $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\ $$ | $$$$$$\ $$\ $$\ +$$$$$\ $$ | $$ |$$ __$$\ $$ __$$\ $$ | $$ | $$$$$\ $$ __$$\ \$$\ $$ | +$$ __|$$ | $$ |$$ | \__|$$ | \__|$$ | $$ | $$ __|$$ / $$ | \$$$$ / +$$ | $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ | $$ | $$ $$< +$$ | \$$$$$$ |$$ | $$ | \$$$$$$$ | $$ | \$$$$$$ |$$ /\$$\ +\__| \______/ \__| \__| \____$$ | \__| \______/ \__/ \__| + $$\ $$ | + \$$$$$$ | + \______/ + +$$$$$$$$\ $$\ $$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ +$$ _____| \__| $$ | $$$\ $$ |$$ _____|\__$$ __| +$$ | $$$$$$\ $$\ $$$$$$\ $$$$$$$\ $$$$$$$ | $$$$$$$\ $$$$\ $$ |$$ | $$ | +$$$$$\ $$ __$$\ $$ |$$ __$$\ $$ __$$\ $$ __$$ |$$ _____| $$ $$\$$ |$$$$$\ $$ | +$$ __|$$ | \__|$$ |$$$$$$$$ |$$ | $$ |$$ / $$ |\$$$$$$\ $$ \$$$$ |$$ __| $$ | +$$ | $$ | $$ |$$ ____|$$ | $$ |$$ | $$ | \____$$\ $$ |\$$$ |$$ | $$ | +$$ | $$ | $$ |\$$$$$$$\ $$ | $$ |\$$$$$$$ |$$$$$$$ | $$ | \$$ |$$ | $$ | +\__| \__| \__| \_______|\__| \__| \_______|\_______/ \__| \__|\__| \__| + +*/ + +// You didn't get on the presale for the FurryFoxFriends NFTs? +// No worries, you can still get one! +contract FurryFoxFriends is ERC721("FurryFoxFriends", "F3"), Ownable2Step { + using MerkleProof for bytes32; + + bytes32 merkleRoot; + uint256 totalSupply; + bool isPublicSale; + mapping(address => bool) alreadyMinted; + mapping(bytes32 => bool) alreadyUsedLeaf; + + function openPresale() external onlyOwner { + isPublicSale = true; + } + + function setNewMerkleRoot(bytes32 newMerkleRoot) external onlyOwner { + merkleRoot = newMerkleRoot; + } + + function presaleMint(bytes32[] calldata proof, bytes32 leaf) external { + require(MerkleProof.verifyCalldata(proof, merkleRoot, leaf), "not verified"); + require(!alreadyMinted[msg.sender], "already minted"); + require(!alreadyUsedLeaf[leaf], "leaf already used"); + + totalSupply++; + _mint(msg.sender, totalSupply - 1); + } + + function mint() external { + require(isPublicSale, "public sale not open"); + + totalSupply++; + _mint(msg.sender, totalSupply - 1); + } +} diff --git a/test/FurryFoxFriends.js b/test/FurryFoxFriends.js new file mode 100644 index 0000000..d11b7b9 --- /dev/null +++ b/test/FurryFoxFriends.js @@ -0,0 +1,35 @@ +const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); +const { expect } = require("chai"); +const { ethers } = require("hardhat"); + +const NAME = "FurryFoxFriends Test"; + +describe(NAME, function () { + async function setup() { + const [, attackerWallet] = await ethers.getSigners(); + const value = ethers.utils.parseEther("1"); + + const nftFactory = await ethers.getContractFactory("FurryFoxFriends"); + const nftContract = await nftFactory.deploy(); + + return { nftContract, attackerWallet }; + } + + describe("exploit", async function () { + let nftContract, attackerWallet; + before(async function () { + ({ walletContract, nftContract, attackerWallet } = await loadFixture(setup)); + const attackerBalance = await nftContract.balanceOf(attackerWallet.address); + expect(attackerBalance).to.be.equal(0, "attacker starts with no NFT"); + }); + + it("conduct your attack here", async function () { + // do it + }); + + after(async function () { + const attackerBalance = await nftContract.balanceOf(attackerWallet.address); + expect(attackerBalance).to.be.greaterThanOrEqual(1, "attacker must acquire an NFT"); + }); + }); +});