Skip to content

Instantly share code, notes, and snippets.

@farzaa
Last active July 17, 2023 18:42
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save farzaa/5015532446dfdb267711592107a285a9 to your computer and use it in GitHub Desktop.
Save farzaa/5015532446dfdb267711592107a285a9 to your computer and use it in GitHub Desktop.
import './styles/App.css';
import twitterLogo from './assets/twitter-logo.svg';
import { ethers } from "ethers";
import React, { useEffect, useState } from "react";
import myEpicNft from './utils/MyEpicNFT.json';
const TWITTER_HANDLE = '_buildspace';
const TWITTER_LINK = `https://twitter.com/${TWITTER_HANDLE}`;
const OPENSEA_LINK = '';
const TOTAL_MINT_COUNT = 50;
// I moved the contract address to the top for easy access.
const CONTRACT_ADDRESS = "0xF1aD06077E05ebD0e0c0e8eBC104fE436c560D6F";
const App = () => {
const [currentAccount, setCurrentAccount] = useState("");
const checkIfWalletIsConnected = async () => {
const { ethereum } = window;
if (!ethereum) {
console.log("Make sure you have metamask!");
return;
} else {
console.log("We have the ethereum object", ethereum);
}
const accounts = await ethereum.request({ method: 'eth_accounts' });
if (accounts.length !== 0) {
const account = accounts[0];
console.log("Found an authorized account:", account);
setCurrentAccount(account)
// Setup listener! This is for the case where a user comes to our site
// and ALREADY had their wallet connected + authorized.
setupEventListener()
} else {
console.log("No authorized account found")
}
}
const connectWallet = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
alert("Get MetaMask!");
return;
}
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
console.log("Connected", accounts[0]);
setCurrentAccount(accounts[0]);
// Setup listener! This is for the case where a user comes to our site
// and connected their wallet for the first time.
setupEventListener()
} catch (error) {
console.log(error)
}
}
// Setup our listener.
const setupEventListener = async () => {
// Most of this looks the same as our function askContractToMintNft
try {
const { ethereum } = window;
if (ethereum) {
// Same stuff again
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const connectedContract = new ethers.Contract(CONTRACT_ADDRESS, myEpicNft.abi, signer);
// THIS IS THE MAGIC SAUCE.
// This will essentially "capture" our event when our contract throws it.
// If you're familiar with webhooks, it's very similar to that!
connectedContract.on("NewEpicNFTMinted", (from, tokenId) => {
console.log(from, tokenId.toNumber())
alert(`Hey there! We've minted your NFT and sent it to your wallet. It may be blank right now. It can take a max of 10 min to show up on OpenSea. Here's the link: https://testnets.opensea.io/assets/${CONTRACT_ADDRESS}/${tokenId.toNumber()}`)
});
console.log("Setup event listener!")
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
console.log(error)
}
}
const askContractToMintNft = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const connectedContract = new ethers.Contract(CONTRACT_ADDRESS, myEpicNft.abi, signer);
console.log("Going to pop wallet now to pay gas...")
let nftTxn = await connectedContract.makeAnEpicNFT();
console.log("Mining...please wait.")
await nftTxn.wait();
console.log(nftTxn);
console.log(`Mined, see transaction: https://rinkeby.etherscan.io/tx/${nftTxn.hash}`);
} else {
console.log("Ethereum object doesn't exist!");
}
} catch (error) {
console.log(error)
}
}
useEffect(() => {
checkIfWalletIsConnected();
}, [])
const renderNotConnectedContainer = () => (
<button onClick={connectWallet} className="cta-button connect-wallet-button">
Connect to Wallet
</button>
);
const renderMintUI = () => (
<button onClick={askContractToMintNft} className="cta-button connect-wallet-button">
Mint NFT
</button>
)
return (
<div className="App">
<div className="container">
<div className="header-container">
<p className="header gradient-text">My NFT Collection</p>
<p className="sub-text">
Each unique. Each beautiful. Discover your NFT today.
</p>
{currentAccount === "" ? renderNotConnectedContainer() : renderMintUI()}
</div>
<div className="footer-container">
<img alt="Twitter Logo" className="twitter-logo" src={twitterLogo} />
<a
className="footer-text"
href={TWITTER_LINK}
target="_blank"
rel="noreferrer"
>{`built on @${TWITTER_HANDLE}`}</a>
</div>
</div>
</div>
);
};
export default App;
pragma solidity 0.8.0;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "hardhat/console.sol";
import { Base64 } from "./libraries/Base64.sol";
contract MyEpicNFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
string baseSvg = "<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 350 350'><style>.base { fill: white; font-family: serif; font-size: 24px; }</style><rect width='100%' height='100%' fill='black' /><text x='50%' y='50%' class='base' dominant-baseline='middle' text-anchor='middle'>";
string[] firstWords = ["BLAH", "BLAH", "BLAH"];
string[] secondWords = ["BLAH", "BLAH", "BLAH"];
string[] thirdWords = ["BLAH", "BLAH", "BLAH"];
// MAGICAL EVENTS.
event NewEpicNFTMinted(address sender, uint256 tokenId);
constructor() ERC721 ("SquareNFT", "SQUARE") {
console.log("This is my NFT contract. Woah!");
}
function pickRandomFirstWord(uint256 tokenId) public view returns (string memory) {
uint256 rand = random(string(abi.encodePacked("FIRST_WORD", Strings.toString(tokenId))));
rand = rand % firstWords.length;
return firstWords[rand];
}
function pickRandomSecondWord(uint256 tokenId) public view returns (string memory) {
uint256 rand = random(string(abi.encodePacked("SECOND_WORD", Strings.toString(tokenId))));
rand = rand % secondWords.length;
return secondWords[rand];
}
function pickRandomThirdWord(uint256 tokenId) public view returns (string memory) {
uint256 rand = random(string(abi.encodePacked("THIRD_WORD", Strings.toString(tokenId))));
rand = rand % thirdWords.length;
return thirdWords[rand];
}
function random(string memory input) internal pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(input)));
}
function makeAnEpicNFT() public {
uint256 newItemId = _tokenIds.current();
string memory first = pickRandomFirstWord(newItemId);
string memory second = pickRandomSecondWord(newItemId);
string memory third = pickRandomThirdWord(newItemId);
string memory combinedWord = string(abi.encodePacked(first, second, third));
string memory finalSvg = string(abi.encodePacked(baseSvg, combinedWord, "</text></svg>"));
string memory json = Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "',
combinedWord,
'", "description": "A highly acclaimed collection of squares.", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(finalSvg)),
'"}'
)
)
)
);
string memory finalTokenUri = string(
abi.encodePacked("data:application/json;base64,", json)
);
console.log("\n--------------------");
console.log(finalTokenUri);
console.log("--------------------\n");
_safeMint(msg.sender, newItemId);
_setTokenURI(newItemId, finalTokenUri);
_tokenIds.increment();
console.log("An NFT w/ ID %s has been minted to %s", newItemId, msg.sender);
// EMIT MAGICAL EVENTS.
emit NewEpicNFTMinted(msg.sender, newItemId);
}
}
@josephceth
Copy link

Not a huge issue, but on Line 133 in App.js, the mint-button class should be applied instead of the connect-wallet-button. It caused a small issue when I was trying to apply a mint-button:disabled option when I had reached my max number of mints.

Thank you and keep up the great work!!!

@prathmeshaghao
Copy link

dont forget to add // SPDX-License-Identifier: UNLICENSED at start

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment