Last active
July 1, 2022 13:48
-
-
Save danicuki/d0e1e749ca9f8b6e79fcc0c466e60844 to your computer and use it in GitHub Desktop.
NFT Collection - final
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
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 |
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
// 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