Skip to content

Instantly share code, notes, and snippets.

@danicuki
Last active July 1, 2022 13:48
Show Gist options
  • Save danicuki/d0e1e749ca9f8b6e79fcc0c466e60844 to your computer and use it in GitHub Desktop.
Save danicuki/d0e1e749ca9f8b6e79fcc0c466e60844 to your computer and use it in GitHub Desktop.
NFT Collection - final
import React, { useEffect, useState } from "react"
import "./styles/App.css"
import twitterLogo from "./assets/twitter-logo.svg"
import { ethers } from "ethers"
import myEpicNft from "./utils/MyEpicNFT.json"
// Constants
const TWITTER_HANDLE = "web3dev_"
const TWITTER_LINK = `https://twitter.com/${TWITTER_HANDLE}`
const OPENSEA_LINK = ""
const TOTAL_MINT_COUNT = 50
// Eu movi o endereço do contrato para cima para ficar fácil acessar
const CONTRACT_ADDRESS = "0xa3a198B75629AA375f96cc46822A79348A90faC3"
const App = () => {
/*
* Só uma variável de estado que usamos pra armazenar nossa carteira pública. Não esqueça de importar o useState.
*/
const [currentAccount, setCurrentAccount] = useState("")
const checkIfWalletIsConnected = async () => {
/*
* Primeiro tenha certeza que temos acesso a window.ethereum
*/
const { ethereum } = window
if (!ethereum) {
console.log("Certifique-se que você tem metamask instalado!")
return
} else {
console.log("Temos o objeto ethereum!", ethereum)
}
/*
* Checa se estamos autorizados a carteira do usuário.
*/
const accounts = await ethereum.request({ method: "eth_accounts" })
/*
* Usuário pode ter múltiplas carteiras autorizadas, nós podemos pegar a primeira que está lá!
*/
if (accounts.length !== 0) {
const account = accounts[0]
console.log("Encontrou uma conta autorizada:", account)
setCurrentAccount(account)
// Setup listener! Isso é para quando o usuário vem no site
// e já tem a carteira conectada e autorizada
setupEventListener()
} else {
console.log("Nenhuma conta autorizada foi encontrada")
}
}
const connectWallet = async () => {
try {
const { ethereum } = window
if (!ethereum) {
alert("Baixe o Metamask!")
return
}
/*
* Método chique para pedir acesso a conta.
*/
const accounts = await ethereum.request({
method: "eth_requestAccounts",
})
/*
* Boom! Isso deve escrever o endereço público uma vez que autorizar o Metamask.
*/
console.log("Conectado", accounts[0])
setCurrentAccount(accounts[0])
// Setup listener! Para quando o usuário vem para o site
// e conecta a carteira pela primeira vez
setupEventListener()
} catch (error) {
console.log(error)
}
}
// Setup do listener.
const setupEventListener = async () => {
// é bem parecido com a função
try {
const { ethereum } = window
if (ethereum) {
// mesma coisa de novo
const provider = new ethers.providers.Web3Provider(ethereum)
const signer = provider.getSigner()
const connectedContract = new ethers.Contract(CONTRACT_ADDRESS, myEpicNft.abi, signer)
// Aqui está o tempero mágico.
// Isso essencialmente captura nosso evento quando o contrato lança
// Se você está familiar com webhooks, é bem parecido!
connectedContract.on("NewEpicNFTMinted", (from, tokenId) => {
console.log(from, tokenId.toNumber())
alert(
`Olá pessoal! Já cunhamos seu NFT. Pode ser que esteja branco agora. Demora no máximo 10 minutos para aparecer no OpenSea. Aqui está o link: <https://testnets.opensea.io/assets/${CONTRACT_ADDRESS}/${tokenId.toNumber()}>`
)
})
console.log("Setup event listener!")
} else {
console.log("Objeto ethereum não existe!")
}
} 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("Vai abrir a carteira agora para pagar o gás...")
let nftTxn = await connectedContract.makeAnEpicNFT()
console.log("Cunhando...espere por favor.")
await nftTxn.wait()
console.log(`Cunhado, veja a transação: https://rinkeby.etherscan.io/tx/${nftTxn.hash}`)
} else {
console.log("Objeto ethereum não existe!")
}
} catch (error) {
console.log(error)
}
}
// Métodos para Renderizar
const renderNotConnectedContainer = () => (
<button onClick={connectWallet} className="cta-button connect-wallet-button">
Conectar Carteira
</button>
)
/*
* Isso roda nossa função quando a página carrega.
*/
useEffect(() => {
checkIfWalletIsConnected()
}, [])
return (
<div className="App">
<div className="container">
<div className="header-container">
<p className="header gradient-text">Minha Coleção de NFT</p>
<p className="sub-text">Exclusivos! Maravilhosos! Únicos! Descubra seu NFT hoje.</p>
{currentAccount === "" ? (
renderNotConnectedContainer()
) : (
<button onClick={askContractToMintNft} className="cta-button connect-wallet-button">
Cunhar NFT
</button>
)}
</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"
>{`feito com ❤️ pela @${TWITTER_HANDLE}`}</a>
</div>
</div>
</div>
)
}
export default App
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.1;
// Precisamos de algumas funcoes utilitarias.
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "hardhat/console.sol";
// Precisamos importar essa funcao de base64 que acabamos de criar
import { Base64 } from "./libraries/Base64.sol";
contract MyEpicNFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
// Aqui esta o codigo do nosso SVG. So precisaremos alterar as palavras que vao ser exibidas. Todo o resto permanece igual.
// Entao, fazemos uma variavel baseSvg aqui que todos os nossos NFTs vao usar.
string baseSvg = "<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMinYMin meet' viewBox='0 0 350 350'> <defs> <linearGradient id='Gradient1'> <stop class='stop1' offset='0%'/> <stop class='stop2' offset='50%'/> <stop class='stop3' offset='100%'/> </linearGradient> </defs> <style> .base { fill: blue; font-family: serif; font-size: 20px; color: #FFF; } .stop1 { stop-color: green; } .stop2 { stop-color: white; stop-opacity: 0; } .stop3 { stop-color: yellow; } </style> <rect width='100%' height='100%' fill='url(#Gradient1)' /> <text x='50%' y='50%' class='base' dominant-baseline='middle' text-anchor='middle' >";
// Eu crio tres listas, cada uma com seu grupo de palavras aleatorias
// escolha as suas palavras divertidas, nome de personagem, comida, time de futebol, o que quiser!
string[] firstWords = ["Tubaina 1", "Guarana", "Fanta", "Doly", "Grapete"];
string[] secondWords = ["Moqueca", "Feijoada", "Vatapa", "Acaraje", "Rabada", "Dobradinha"];
string[] thirdWords = ["Maracuja", "Pitanga", "Graviola", "Acai", "Banana", "Amora"];
event NewEpicNFTMinted(address sender, uint256 tokenId);
constructor() ERC721 ("ChavesNFT", "CHAVO") {
console.log("Meu contrato de NFT! Tchu-hu");
}
// Crio uma funcao que pega uma palavra aleatoria da lista.
function pickRandomFirstWord(uint256 tokenId) public view returns (string memory) {
// Crio a 'semente' para o gerador aleatorio. Mais sobre isso na licao.
uint256 rand = random(string(abi.encodePacked("PRIMEIRA_PALAVRA", Strings.toString(tokenId))));
// pego o numero no maximo ate o tamanho da lista, para nao dar erro de indice.
rand = rand % firstWords.length;
return firstWords[rand];
}
function pickRandomSecondWord(uint256 tokenId) public view returns (string memory) {
uint256 rand = random(string(abi.encodePacked("SEGUNDA_PALAVRA", 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("TERCEIRA_PALAVRA", 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();
// Agora pegamos uma palavra aleatoria de cada uma das 3 listas.
string memory first = pickRandomFirstWord(newItemId);
string memory second = pickRandomSecondWord(newItemId);
string memory third = pickRandomThirdWord(newItemId);
string memory combinedWord = string(abi.encodePacked(first, second, third));
// Concateno tudo junto e fecho as tags <text> e <svg>.
string memory finalSvg = string(abi.encodePacked(baseSvg, combinedWord, "</text></svg>"));
// pego todos os metadados de JSON e codifico com base64.
string memory json = Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "',
// Definimos aqui o titulo do nosso NFT sendo a combinacao de palavras.
combinedWord,
'", "description": "Uma colecao aclamada e famosa de NFTs maravilhosos.", "image": "data:image/svg+xml;base64,',
// Adicionamos data:image/svg+xml;base64 e acrescentamos nosso svg codificado com base64.
Base64.encode(bytes(finalSvg)),
'"}'
)
)
)
);
// Assim como antes, prefixamos com data:application/json;base64
string memory finalTokenUri = string(
abi.encodePacked("data:application/json;base64,", json)
);
console.log("\n--------------------");
console.log(finalTokenUri);
console.log("--------------------\n");
_safeMint(msg.sender, newItemId);
// AQUI VAI A NOVA URI DINAMICAMENTE GERADA!!!
_setTokenURI(newItemId, finalTokenUri);
_tokenIds.increment();
console.log("Um NFT com ID %s foi cunhado para %s", newItemId, msg.sender);
emit NewEpicNFTMinted(msg.sender, newItemId);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment