Vamos colocar todo o conhecimento dos meus artigos anteriores em prática escrevendo e implantando um contrato inteligente, construindo um front-end e cunhando alguns NFTs.
Foto de Killian Cartignies em Unsplash
Se você acompanhou a série Flow até agora, já sabe que a Blockchain Flow se destaca no manuseio de ativos digitais, como NFTs. Foi construída desde o início como uma alternativa melhor ao congestionamento da rede Ethereum e aos problemas de altas taxas.
Além disso, a linguagem de contrato inteligente Cadence é a primeira linguagem de programação orientada a recursos que torna fácil e eficiente a criação e o gerenciamento de ativos digitais. Embora o Solidity seja excelente para facilitar a Web3 por meio de contratos inteligentes, existem desvantagens. A Cadence melhora as falhas do Solidity, fornecendo a capacidade de atualizar contratos inteligentes e recursos que reduzem o risco de erro humano, entre outras melhorias.
E, finalmente, a lista de ferramentas e bibliotecas disponíveis para desenvolvedores que desejam começar é extensa. Então, vamos juntar tudo e construir algo na Flow.
Este artigo é um tutorial sobre como criar um dapp de cunhagem de NFT completo para a Blockchain Flow.
Vamos ao que interessa
No restante deste artigo, vamos percorrer o processo de criação de um dapp de cunhagem de NFT na blockchain Flow.
Começaremos configurando e implantando um contrato inteligente Cadence. Em seguida, construiremos um front-end para conectar ao nosso contrato inteligente e cunhar um NFT na conta do usuário.
A funcionalidade que construímos permitirá que os usuários conectem sua conta da Flow, criem uma conta se ainda não tiverem uma e selecionem uma das três imagens para cunhar um NFT. Em seguida, o dapp exibirá os NFTs do nosso acervo que estão na conta do usuário. Será um excelente projeto para destacar como é fácil e eficiente criar NFTs na Flow e como a Flow Client Library (FCL) é eficaz para interagir com a blockchain.
Para acompanhar este tutorial, você precisará do seguinte:
- NodeJs e NPM
- A Interface de Linha de Comando da Flow (Flow CLI)
- Seu IDE favorito
Com tudo isso instalado, vamos começar!
Configurar conta Flow
Antes de começarmos a construir, precisaremos configurar uma conta na blockchain da Flow para podermos implantar nosso contrato inteligente. Execute o seguinte comando para gerar um novo par de chaves pública e privada:
flow keys generate
Certifique-se de anotar os valores de saída do console, pois precisaremos deles nas etapas a seguir.
Em seguida, iremos para Faucet Flow para criar um novo endereço com base em nossas chaves e financiar nossa conta com alguns tokens de teste. Conclua as etapas a seguir para criar sua conta:
- Cole sua chave pública no campo de entrada especificado
- Mantenha os algoritmos de assinatura e hash definidos como padrão
- Complete o captcha
- Clique em Criar conta
Com uma geração de conta bem-sucedida, obtemos um diálogo com nosso novo endereço Flow contendo 1.000 tokens FLOW.
Copie o endereço para uso na próxima etapa.
Configure o contrato inteligente
Antes de construirmos o front-end do projeto, vamos criar o contrato inteligente com o qual interagiremos posteriormente.
No terminal de comando, navegue até a pasta na qual deseja trabalhar e digite o seguinte comando para iniciar um projeto:
flow init
Este comando cria um arquivo flow.json
dentro da pasta, onde colocaremos todas as informações necessárias para implantar nosso contrato inteligente.
Abra o arquivo flow.json
em seu editor de código e configuraremos uma conta testnet. Dentro da seção accounts
, adicionaremos uma nova entrada chamada testnet-account
, que contém nosso novo endereço e a chave privada gerada nocomando
flow keys generate
anterior.
{
"emulators": {
"default": {
"port": 3569,
"serviceAccount": "emulator-account"
}
},
"contracts": {},
"networks": {
"emulator": "127.0.0.1:3569",
"mainnet": "access.mainnet.nodes.onflow.org:9000",
"testnet": "access.devnet.nodes.onflow.org:9000"
},
"accounts": {
"emulator-account": {
"address": "f8d6e0586b0a20c7",
"key": "2becfbede2fb89796ab68df3ec2a23c3627235ec250a3e5da41df850a8dd4349"
},
"testnet-account": {
"address": "0x8e0dac5df6e8489e",
"key": "c91f4716a51a66683ccb090ca3eb3e213b90e9f9ae2b1edd12defffe06c57edc"
}
},
"deployments": {}
}
Em seguida, criaremos um novo arquivo para escrever nosso contrato inteligente.
Ao escrever o código, você pode notar algumas diferenças em como a Cadence lida com a criação de NFT em comparação com o Solidity. Por exemplo, os NFTs na Cadence são criados como um recurso e cunhados diretamente na conta do usuário. Em contraste, os NFTs do Solidity são essencialmente apenas um número de identificação referenciado em um mapeamento para um endereço específico no registro digital.
Então, pensando nisso, na mesma pasta do arquivo flow.json
, crie um novo arquivo chamado FlowTutorialMint.cdc
, e digite o seguinte código:
/*
*
* Este é um exemplo de implementação de um Token Não Fungível Flow
* Este contrato não implementa nenhuma classificação sofisticada
* para seu sistema NFT. Ele define um NFT simples com metadados mínimos.
*/
import NonFungibleToken from 0x631e88ae7f1d7c20
import MetadataViews from 0x631e88ae7f1d7c20
pub contract FlowTutorialMint: NonFungibleToken {
pub var totalSupply: UInt64
pub event ContractInitialized()
pub event Withdraw(id: UInt64, from: Address?)
pub event Deposit(id: UInt64, to: Address?)
pub let CollectionStoragePath: StoragePath
pub let CollectionPublicPath: PublicPath
pub let MinterStoragePath: StoragePath
pub struct FlowTutorialMintData{
pub let id: UInt64
pub let type: String
pub let url: String
init(_id: UInt64, _type: String, _url: String){
self.id = _id
self.type = _type
self.url = _url
}
}
pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
pub let id: UInt64
pub let type: String
pub let url: String
init(
id: UInt64,
type: String,
url: String,
) {
self.id = id
self.type = type
self.url = url
}
pub fun getViews(): [Type] {
return [ Type<FlowTutorialMintData>() ]
}
pub fun resolveView(_ view: Type): AnyStruct? {
switch view {
case Type<FlowTutorialMintData>():
return FlowTutorialMintData(
_id: self.id,
_type: self.type,
_url: self.url
)
}
return nil
}
}
pub resource interface FlowTutorialMintCollectionPublic {
pub fun deposit(token: @NonFungibleToken.NFT)
pub fun getIDs(): [UInt64]
pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
pub fun borrowFlowTutorialMint(id: UInt64): &FlowTutorialMint.NFT? {
post {
(result == nil) || (result?.id == id):
"Não é possível emprestar a referência FlowTutorialMint: o ID da referência retornada está incorreto"
}
}
}
pub resource Collection: FlowTutorialMintCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
// dicionário de tokens em conformidade com NFT
// NFT é um tipo de recurso com um campo de ID `UInt64`
pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
init () {
self.ownedNFTs <- {}
}
// withdraw remove um NFT da coleção e o move para o chamador
pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
emit Withdraw(id: token.id, from: self.owner?.address)
return <-token
}
// deposit pega um NFT e o adiciona ao dicionário de coleções e adiciona o ID ao array de id
pub fun deposit(token: @NonFungibleToken.NFT) {
let token <- token as! @FlowTutorialMint.NFT
let id: UInt64 = token.id
// adicione o novo token ao dicionário que remove o antigo
let oldToken <- self.ownedNFTs[id] <- token
emit Deposit(id: id, to: self.owner?.address)
destroy oldToken
}
// getIDs retorna um array dos IDs que estão na coleção
pub fun getIDs(): [UInt64] {
return self.ownedNFTs.keys
}
// loanNFT obtém uma referência a um NFT na coleção para que o chamador possa ler seus metadados e chamar seus métodos
pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
}
pub fun borrowFlowTutorialMint(id: UInt64): &FlowTutorialMint.NFT? {
if self.ownedNFTs[id] != nil {
// Crie uma referência autorizada para permitir downcasting
let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
return ref as! &FlowTutorialMint.NFT
}
return nil
}
pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
let flowTutorialMintNFT = nft as! &FlowTutorialMint.NFT
return flowTutorialMintNFT as &AnyResource{MetadataViews.Resolver}
}
destroy() {
destroy self.ownedNFTs
}
}
// função pública que qualquer um pode chamar para criar uma nova coleção vazia
pub fun createEmptyCollection(): @NonFungibleToken.Collection {
return <- create Collection()
}
pub fun mintNFT(
recipient: &{NonFungibleToken.CollectionPublic},
type: String,
url: String,
) {
// criar um novo NFT
var newNFT <- create NFT(
id: FlowTutorialMint.totalSupply,
type: type,
url: url
)
// depositá-lo na conta do destinatário usando sua referência
recipient.deposit(token: <-newNFT)
FlowTutorialMint.totalSupply = FlowTutorialMint.totalSupply + UInt64(1)
}
init() {
// Inicializar o fornecimento total
self.totalSupply = 0
// Definir os caminhos nomeados
self.CollectionStoragePath = /storage/flowTutorialMintCollection
self.CollectionPublicPath = /public/flowTutorialMintCollection
self.MinterStoragePath = /storage/flowTutorialMintMinter
// Criar um recurso de Coleção e salvá-lo no armazenamento
let collection <- create Collection()
self.account.save(<-collection, to: self.CollectionStoragePath)
// criar um recurso público para a coleção
self.account.link<&FlowTutorialMint.Collection{NonFungibleToken.CollectionPublic, FlowTutorialMint.FlowTutorialMintCollectionPublic, MetadataViews.ResolverCollection}>(
self.CollectionPublicPath,
target: self.CollectionStoragePath
)
emit ContractInitialized()
}
}
Coisas importantes a serem observadas no contrato inteligente acima:
- Estamos importando os contratos
NonFungibleToken
eMetadataViews
para criar nossos NFTs usando os padrões Flow - Definimos nosso recurso NFT na função
pub resource NFT
- A função
mintNFT
cria um NFT na conta que chama a função
Agora precisamos voltar ao nosso arquivo flow.json
para adicionar algumas coisas:
- Na seção
contracts
, adicione o contrato e seu caminho. - Na seção
deployments
adicione a rede (testnet
), a conta que usaremos para realizar a implantação (testnet
-account
) e o nome do contrato (FlowTutorialMint
).
{
"emulators": {
"default": {
"port": 3569,
"serviceAccount": "emulator-account"
}
},
"contracts": {
"FlowTutorialMint": "./FlowTutorialMint.cdc"
},
"networks": {
"emulator": "127.0.0.1:3569",
"mainnet": "access.mainnet.nodes.onflow.org:9000",
"testnet": "access.devnet.nodes.onflow.org:9000"
},
"accounts": {
"emulator-account": {
"address": "f8d6e0586b0a20c7",
"key": "2becfbede2fb89796ab68df3ec2a23c3627235ec250a3e5da41df850a8dd4349"
},
"testnet-account": {
"address": "0x8e0dac5df6e8489e",
"key": "c91f4716a51a66683ccb090ca3eb3e213b90e9f9ae2b1edd12defffe06c57edc"
}
},
"deployments": {
"testnet": {
"testnet-account": [
"FlowTutorialMint"
]
}
}
}
A etapa final na configuração do contrato inteligente é implantá-lo na rede de teste. Para fazer isso, digite o seguinte comando na pasta do projeto em seu terminal:
flow project deploy -n=testnet
Devemos receber uma saída informando que o contrato foi implantado com sucesso:
É importante observar aqui que os contratos inteligentes da Cadence existem no armazenamento da conta que os implanta, enquanto, com Solidity, o contrato inteligente existe em seu próprio endereço na blockchain.
Embora haja limites para a capacidade de armazenamento da conta, eles são relativos ao número de tokens FLOW reservados na conta. Você pode aprender mais sobre o armazenamento de contas no Flow Developer Portal.
Incrível! Agora vamos construir um frontend simples para interagir com nosso contrato.
Criando o front-end
Para o front-end deste projeto, usaremos o React. Primeiro, navegue até uma nova pasta e execute o seguinte comando para criar um projeto React:
npx create-react-app flow-tutorial
Em seguida, navegue até a pasta flow-tutorial e instale a Flow Client Library (FCL):
npm i -S @onflow/fcl
O FCL nos permitirá comunicar com a blockchain Flow, chamar transações e integrar todas as outras carteiras compatíveis com FCL sem a necessidade de adicionar integrações personalizadas. Assim que terminar, instalaremos algumas dependências adicionais:
npm i elliptic sha3 styled-components
Depois de instalar todas as nossas dependências, estamos prontos para começar a trabalhar no frontend do dapp.
Configurar o FCL
Antes de começarmos a estruturar e estilizar as coisas, vamos criar um arquivo de configuração FCL onde definiremos configurações importantes, como se iremos interagir com testnet ou mainnet.
No diretório src
, crie uma nova pasta chamada flow
. Dentro desta nova pasta, crie um arquivo chamado config
.js
.
Neste arquivo config
.js
vamos importar o FCL, chamar a função fcl.config
e criar algumas configurações para nosso dapp, como:
app.detail.title
accessNode.api
discovery.wallet
Abra o arquivo config.js
e preencha com o seguinte código:
const fcl = require("@onflow/fcl");
fcl.config({
"app.detail.title": "Flow Mint Page Tutorial", // isto acrescenta um nome personalizado à nossa carteira
"accessNode.api": "https://rest-testnet.onflow.org", // isto é para o emulador local
"discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", // isto é para a carteira local do dev
})
Existem configurações adicionais que podemos configurar para nosso dapp, mas, por enquanto, isso é tudo que precisamos.
Feita a configuração, vamos à construção!
A estrutura inicial
Primeiro, navegue até o arquivo App.js
na pasta src
e substitua o código por este:
import './App.css';
function App() {
return (
<div className="App">
<h1>Mint Your Dog!</h1>
</div>
);
}
export default App;
Isso nos dará a estrutura inicial do nosso dapp, a partir da qual iremos expandir.
A seguir, estilizaremos essa estrutura. Abra o arquivo index.css
e substitua o código pelo seguinte:
@import url('https://fonts.googleapis.com/css2?family=Michroma&family=Montserrat:wght@200;300;600;700&display=swap');
body {
margin: 0;
font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
Se você executar npm start, verá uma página em branco com o título Mint Your Dog!
Em seguida, vamos criar alguns componentes!
O componente nav
Dentro do diretório src
, crie uma nova pasta chamada components
, onde construiremos todos os nossos componentes personalizados do React.
O primeiro componente que iremos criar é a Navbar, que mostrará o botão Login caso o usuário não esteja conectado, ou o botão Logout ao lado do endereço do usuário e a quantidade de tokens FLOW que a conta possui caso esteja conectado.
Crie um arquivo chamado Navbar.jsx
e preencha-o com o seguinte código:
import * as fcl from "@onflow/fcl";
import styled from "styled-components";
import { useState, useEffect } from "react";
import "../flow/config";
const Wrapper = styled.nav`
width: -webkit-fill-available;
background-color: #8dfe89;
position: fixed;
top: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 50px;
button {
background-color: white;
padding: 5px 40px;
max-width: 200px;
border: none;
border-radius: 20px;
font-size: 18px;
height: 50px;
&:hover {
color: white;
background-color: black;
cursor: pointer;
}
}
div {
display: flex;
gap: 15px;
}
box {
display: flex;
flex-direction: column;
gap: 10px;
}
`;
function Navbar() {
const [user, setUser] = useState({ loggedIn: false, addr: undefined });
const [flow, setFlow] = useState(0);
useEffect(() => {
fcl.currentUser.subscribe(setUser);
if (user.addr !== "") getFlow(user.addr);
}, [user.addr]);
const logOut = async () => {
await fcl.unauthenticate();
setUser({ addr: undefined, loggedIn: false });
};
const logIn = async () => {
await fcl.authenticate();
};
async function getFlow(address) {
try {
const res = await fcl.query({
cadence: `
import FlowToken from 0x7e60df042a9c0868
import FungibleToken from 0x9a0766d93b6608b7
pub fun main(address: Address): UFix64{
let balanceVault = getAccount(address).getCapability(/public/flowTokenBalance).borrow<&FlowToken.Vault{FungibleToken.Balance}>()!
return balanceVault.balance
}`,
args: (arg, t) => [arg(address, t.Address)],
});
setFlow(res);
} catch (error) {
console.log("err:", error);
}
}
return (
<Wrapper>
<h1>Flow Tutorial Mint</h1>
{user.loggedIn ? (
<div>
<button onClick={() => logOut()}>Logout</button>
<box>
<span>Address - {user.addr}</span>
<span>Flow Balance - {flow}</span>
</box>
</div>
) : (
<button onClick={() => logIn()}>Login</button>
)}
</Wrapper>
);
}
export default Navbar;
Vamos percorrer o código para ver o que está acontecendo aqui.
- Primeiro, estamos importando a Flow Client Library, que nos fornecerá funções para
authenticate
,unauthenticate
, e determinará ocurrentUser
. - Em seguida, importamos as outras dependências de que precisamos e usamos
styled-component
s para criar o estilo básico de nossa Navbar dentro da variávelWrapper
. - Em seguida, definimos algumas variáveis de estado do React (
user
eflow
). - Em seguida, vem a funcionalidade do dapp, como
logOut
,logIn
egetFlow
(obter o saldo de FLOW da conta conectada). - Depois disso, retornamos o
html
para o Navbar envolto em nosso estilo. - Com um componente
Navbar
completo, agora podemos importá-lo para o arquivoApp
.js
:
import './App.css';
import Navbar from './components/Navbar.jsx';
function App() {
return (
<div className="App">
<Navbar />
<h1>Mint your Dog!</h1>
</div>
);
}
export default App;
Agora, se executarmos o projeto com npm start
, veremos que nosso Navbar
nos dá a funcionalidade que definimos em nosso código. Incrível!
Em seguida, vamos construir nosso componente de cunhagem de NFT!
O componente de cunhagem de NFT
Dentro da pasta components
, crie um novo arquivo chamado MintComponent.jsx
, a seguir copie o seguinte código:
import styled from "styled-components";
import * as fcl from "@onflow/fcl";
const Wrapper = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
justify-content: center;
margin-top: 80px;
padding: 100px;
main{
display: flex;
}
div{
width: 300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
}
button{
width: 100px;
padding: 10px;
border: none;
background-color: #8dfe89;
border-radius: 20px;
font-weight: 500;
&:hover {
color: white;
background-color: black;
cursor: pointer;
}
}
img{
width: 200px;
}
`;
function MintComponent() {
async function mintNFT(type, url) {
try {
const res = await fcl.mutate({
cadence: `
import FlowTutorialMint from 0x8e0dac5df6e8489e
import NonFungibleToken from 0x631e88ae7f1d7c20
import MetadataViews from 0x631e88ae7f1d7c20
transaction(type: String, url: String){
let recipientCollection: &FlowTutorialMint.Collection{NonFungibleToken.CollectionPublic}
prepare(signer: AuthAccount){
if signer.borrow<&FlowTutorialMint.Collection>(from: FlowTutorialMint.CollectionStoragePath) == nil {
signer.save(<- FlowTutorialMint.createEmptyCollection(), to: FlowTutorialMint.CollectionStoragePath)
signer.link<&FlowTutorialMint.Collection{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>(FlowTutorialMint.CollectionPublicPath, target: FlowTutorialMint.CollectionStoragePath)
}
self.recipientCollection = signer.getCapability(FlowTutorialMint.CollectionPublicPath)
.borrow<&FlowTutorialMint.Collection{NonFungibleToken.CollectionPublic}>()!
}
execute{
FlowTutorialMint.mintNFT(recipient: self.recipientCollection, type: type, url: url)
}
}
`,
args: (arg, t) => [arg(type, t.String), arg(url, t.String)],
limit: 9999,
});
fcl.tx(res).subscribe((res) => {
if (res.status === 4 && res.errorMessage === "") {
window.alert("NFT Minted!")
window.location.reload(false);
}
});
console.log("txid", res);
} catch (error) {
console.log("err", error);
}
}
return (
<Wrapper>
<h1>Mint your Dog!</h1>
<main>
<div>
<img src="https://images.unsplash.com/photo-1517849845537-4d257902454a" alt="Mad Dog"/>
<h3>Mad Dog</h3>
<button onClick={() => mintNFT("Mad Dog", "https://images.unsplash.com/photo-1517849845537-4d257902454a")}>Mint</button>
</div>
<div>
<img src="https://images.unsplash.com/photo-1517423568366-8b83523034fd" alt="Swag Dog"/>
<h3>Swag Dog</h3>
<button onClick={() => mintNFT("Swag Dog", "https://images.unsplash.com/photo-1517423568366-8b83523034fd")}>Mint</button>
</div>
<div>
<img src="https://images.unsplash.com/photo-1517519014922-8fc06b814a0e" alt="French Dog"/>
<h3>French Dog</h3>
<button onClick={() => mintNFT("French Dog", "https://images.unsplash.com/photo-1517519014922-8fc06b814a0e")}>Mint</button>
</div>
</main>
</Wrapper>
)
}
export default MintComponent;
Novamente, vamos percorrer o código para garantir que entendemos o que está acontecendo.
- Precisamos importar o FCL neste componente para ter acesso à função que nos permitirá cunhar nosso NFT.
- Novamente, usamos
styled-components
para adicionar algum estilo.
A função mintNFT
usa a função fcl.mutate
para executar a cunhagem real por:
- Validar se o usuário tem uma coleção Flow Tutorial Mint NFT em sua conta e criar uma, caso não tenha.
- Chamando a função mint existente dentro do contrato FlowTutorialMint e passando os parâmetros.
- A função retorna o recurso (NFT), que depositamos na conta do usuário.
- Na função
fcl.mutate
, estamos importando o contrato inteligente que implantamos com a linha:import FlowTutorialMint from 0x8e0dac5df6e8489e
- Também importamos os padrões
NonFngibleToken
eMetadataViews
. - Na transação especificamos o NFT
type
eurl
da imagem.
As transações de Cadence têm duas fases: prepare
e execute
-
prepare
– pedimos a assinatura do usuário para acessar sua conta e realizar funções privadas. Nesse caso, criar uma nova coleçãoFlowTutorial
de Cunhagem, caso ainda não tenha uma. Também inicializamos umCapability
público restrito aNonFungibleToken.CollectionPublic
. Para obter mais contexto sobre Capabilities, confira este link. -
execute
– chame a funçãomintNFT
dentro do nosso contrato na testnet. - Na parte
html
do código, exibimos três imagens das quais o usuário pode cunhar um NFT.
Com o nosso MintComponent
completo, podemos importá-lo para oarquivo
App.js
:
import './App.css';
import Navbar from './components/Navbar.jsx';
import MintComponent from './components/MintComponent.jsx';
function App() {
return (
<div className="App">
<Navbar />
<h1>Mint your Dog!</h1>
<MintComponent />
</div>
);
}
export default App;
Agora o usuário pode fazer login no dapp e cunhar um NFT em sua conta!
A peça final do quebra-cabeça é criar um componente que irá buscar os NFTs do usuário e exibi-los.
Mostrando os NFTs do usuário
Na pasta components
, crie um novo arquivo chamado ShowNfts.jsx
, e usaremos o seguinte código:
import * as fcl from "@onflow/fcl";
import { useState, useEffect } from "react";
import styled from "styled-components";
const Wrapper = styled.div`
background-color: #e5e5e5;
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
justify-content: center;
padding: 50px;
button {
width: 100px;
padding: 10px;
border: none;
background-color: #8dfe89;
border-radius: 10px;
font-weight: 700;
&:hover {
color: white;
background-color: black;
cursor: pointer;
}
}
section {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 30px;
padding: 10%;
}
.nftDiv{
padding: 10px;
background-color: #141414;
border-radius: 20px;
color: white;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.25);
img{
width: 140px;
border-radius: 10px;
}
p{
font-size: 14px;
}
}
`;
export default function ShowNfts() {
const [nfts, setNfts] = useState([]);
const [user, setUser] = useState({ loggedIn: false, addr: undefined });
useEffect(() => {
fcl.currentUser.subscribe(setUser);
getNFTs(user.addr)
}, [user.addr]);
async function getNFTs(addr) {
try {
const result = await fcl.query({
cadence: `
import FlowTutorialMint from 0x8e0dac5df6e8489e
import MetadataViews from 0x631e88ae7f1d7c20
pub fun main(address: Address): [FlowTutorialMint.FlowTutorialMintData] {
let collection = getAccount(address).getCapability(FlowTutorialMint.CollectionPublicPath)
.borrow<&{MetadataViews.ResolverCollection}>()
?? panic("Não foi possível emprestar uma referência à coleção NFT")
let ids = collection.getIDs()
let answer: [FlowTutorialMint.FlowTutorialMintData] = []
for id in ids {
let nft = collection.borrowViewResolver(id: id)
let view = nft.resolveView(Type<FlowTutorialMint.FlowTutorialMintData>())!
let display = view as! FlowTutorialMint.FlowTutorialMintData
answer.append(display)
}
return answer
}
`,
args: (arg, t) => [arg(addr, t.Address)],
});
setNfts(result);
} catch (error) {
console.log("err", error);
}
}
return (
<Wrapper>
<h1>My NFTs</h1>
<main>
<button onClick={() => getNFTs(user.addr)}>Get NFTs</button>
<section>
{nfts.map((nft, index) => {
return (
<div key={index} className="nftDiv">
<img src={nft.url} alt="nft" />
<p>Type: {nft.type}</p>
<p>Id: {nft.id}</p>
</div>
);
})}
</section>
</main>
</Wrapper>
);
}
Essencialmente, o que estamos fazendo neste código é consultar a Blockchain Flow usando o FCL e reunir os NFTs na conta conectada que são de nossa coleção FlowTutorialMint
.
Só precisamos adicionar este componente ao nosso App.js
, e estamos prontos!
import './App.css';
import Navbar from './components/Navbar.jsx';
import MintComponent from './components/MintComponent.jsx';
import ShowNfts from './components/ShowNfts';
function App() {
return (
<div className="App">
<Navbar />
<h1>Mint your Dog!</h1>
<MintComponent />
<ShowNfts />
</div>
);
}
export default App;
Isso é tudo! Agora vamos testar nosso dapp e garantir que podemos cunhar alguns NFTs.
Vamos cunhar alguns NFTs!
Então, primeiro, vamos iniciar o aplicativo com npm start
e, em seguida, abrir nosso navegador para http://localhost:3000/
.
Se tudo correr bem, sua tela deve ficar assim:
A beleza de usar o FCL em nossa sequência de login é que ele oferece aos nossos usuários acesso fácil para criar uma conta no local usando apenas um endereço de e-mail. Vamos percorrer o processo para garantir que funcione corretamente. Ao clicar no botão Login, uma caixa de diálogo aparecerá, dando-nos duas opções para fazer o login. Vamos escolher Blocto.
O Blocto solicitará que insiramos um endereço de e-mail e, ao fazê-lo, nos dará a possibilidade de registrar uma nova conta. Então, assim que inserirmos o código enviado por e-mail para o nosso endereço, o Blocto nos configura um novo e brilhante endereço da Flow!
A partir daqui, podemos escolher qual imagem de cachorro queremos cunhar como NFT. Escolhi o Swag Dog porque me lembra um pouco de mim mesmo!
Pressionar o botão Mint abrirá outro diálogo informando sobre a transação que estamos prestes a realizar. Podemos ver que o Blocto está graciosamente cobrindo as taxas de cunhagem e, se quisermos ver o script que estamos chamando, podemos fazê-lo.
Vários segundos depois de clicar em Approve, devemos receber uma mensagem de que nossa cunhagem foi bem-sucedida e nosso recém-criado Swag Dog será exibido na seção Meus NFTs de nosso dapp.
Aqui está um link para o nosso dapp em ação:
https://s1.gifyu.com/images/flow_tutorial-min.gif
Todo o código-fonte deste projeto pode ser encontrado neste repositório.
Conclusão
Como você pode ver, construir um dapp de cunhagem de NFT na Blockchain Flow é simples, uma vez que você entende como tudo funciona junto. Além disso, a Flow Client Library é uma ferramenta poderosa à nossa disposição que nos dá acesso a uma ampla funcionalidade integrada e ajuda a proporcionar ao nosso dapp uma melhor experiência do usuário.
Ao contrário da Ethereum, a Flow lida com a criação e o gerenciamento de NFT com muito mais eficiência e segurança. Isso é obtido com a implantação de contratos inteligentes e a cunhagem de NFTs diretamente na conta do usuário, em vez de criar uma referência a endereços ou mapeamentos armazenados no livro-razão digital.
Para obter mais informações sobre como criar na Flow, confira o Flow Developer Portal.
Tenha um dia realmente fantástico!
Artigo escrito por John Vester. Traduzido por Marcelo Panegali
Top comments (0)