WEB3DEV

Cover image for Como Construir um Dapp de Cunhagem de NFT na Blockchain Flow
Panegali
Panegali

Posted on

Como Construir um Dapp de Cunhagem de NFT na Blockchain Flow

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.

1

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:

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Cole sua chave pública no campo de entrada especificado
  2. Mantenha os algoritmos de assinatura e hash definidos como padrão
  3. Complete o captcha
  4. Clique em Criar conta

2

Com uma geração de conta bem-sucedida, obtemos um diálogo com nosso novo endereço Flow contendo 1.000 tokens FLOW.

3

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
Enter fullscreen mode Exit fullscreen mode

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": {}
}
Enter fullscreen mode Exit fullscreen mode

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()
    }
}
Enter fullscreen mode Exit fullscreen mode

Coisas importantes a serem observadas no contrato inteligente acima:

  • Estamos importando os contratos NonFungibleToken e MetadataViews 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"
  ]
 }
}
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Devemos receber uma saída informando que o contrato foi implantado com sucesso:

r

É 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
Enter fullscreen mode Exit fullscreen mode

Em seguida, navegue até a pasta flow-tutorial e instale a Flow Client Library (FCL):

npm i -S @onflow/fcl
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.confige 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
})
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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á o currentUser.
  • Em seguida, importamos as outras dependências de que precisamos e usamos styled-components para criar o estilo básico de nossa Navbar dentro da variável Wrapper.
  • Em seguida, definimos algumas variáveis ​​de estado do React ( user e flow).
  • Em seguida, vem a funcionalidade do dapp, como logOut, logIne getFlow (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 arquivo App.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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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 mintNFTusa 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 e MetadataViews.
  • Na transação especificamos o NFT typee urlda imagem.

As transações de Cadence têm duas fases: preparee execute

  • prepare– pedimos a assinatura do usuário para acessar sua conta e realizar funções privadas. Nesse caso, criar uma nova coleção FlowTutorial de Cunhagem, caso ainda não tenha uma. Também inicializamos um Capability público restrito a NonFungibleToken.CollectionPublic. Para obter mais contexto sobre Capabilities, confira este link.
  • execute – chame a função mintNFT 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 MintComponentcompleto, 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;
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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:

ut

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.

sda

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!

k

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!

hd

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.

09

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)