WEB3DEV

Cover image for Execução de Tarefa de Teste de NFT em Solidity: Percepções de um Desenvolvedor de Blockchain Profissional
Fatima Lima
Fatima Lima

Posted on

Execução de Tarefa de Teste de NFT em Solidity: Percepções de um Desenvolvedor de Blockchain Profissional

Nos últimos três anos, tenho ocupado posições de liderança de equipes de programação em várias empresas estatais. Como parte de minhas funções, realizei inúmeras entrevistas com candidatos a emprego e os atribuí tarefas de teste para uma avaliação mais profunda. No entanto, descobri que os resultados dessas atribuições eram muitas vezes medíocres e executadas apressadamente.

Recentemente, deparei-me com um caso de teste para uma empresa de blockchain em busca de um desenvolvedor Solidity. Isto me inspirou a escrever um artigo e compartilhar minhas idéias sobre como se sobressair nestes tipos de testes. E se eu pudesse mostrar às pessoas como se destacar na multidão de submissões?

Então, vamos começar! Tenho algumas dicas e truques para compartilhar que o ajudarão a criar atribuições de testes impressionantes que demonstrem suas habilidades e conhecimentos.

Tarefa de teste em Solidity
 1. Desenvolva um ou mais contratos inteligentes em Solidity para a implantação de uma coleção NFT (ERC721) com alguns argumentos (nome, símbolo). O contrato inteligente deve emitir os seguintes eventos:
     a. CollectionCreated (endereço da coleção, nome, símbolo)
     b. TokenMinted (endereço da coleção, endereço do destinatário, tokenId, tokenUri)
 2. Desenvolva um servidor backend simples com armazenamento em memória para lidar com eventos emitidos e servi-lo via HTTP.
 3. Desenvolva um aplicativo de demonstração front-end que interaja com o contrato inteligente e tenha a seguinte funcionalidade:
     a. Criar uma nova coleção NFT com nome e símbolo específicos (a partir da entrada do usuário);
     b.Cunhar um novo NFT com endereço de coleção específico (apenas criado em 3.a), tokenId,tokenUri.
Enter fullscreen mode Exit fullscreen mode

Para verificar o resultado final, basta clicar no link a seguir:

https://aleksanderborovoy.github.io/ethereum-nft/

Este artigo fornecerá uma análise detalhada do processo de criação de um contrato inteligente. Além disso, um repositório com um frontend será anexado para sua conveniência.

De acordo com a tarefa, precisamos criar uma fábrica que nos permitirá implantar contratos ERC721. Para começar, copiaremos o repositório do hardhat inicial da minha conta no GitHub.

git clone https://github.com/AleksanderBorovoy/hardhat-starter-kit.git nft-collection
Enter fullscreen mode Exit fullscreen mode

A seguir, vá até a pasta e instale as dependências.

cd nft-collection && yarn install
Enter fullscreen mode Exit fullscreen mode

Em seguida, vamos usar https://www.openzeppelin.com/contracts para gerar um contrato inteligente de NFT.

ERC721

Após copiar o código para o arquivo NFTCollection.sol, precisamos adicionar um evento TokenMinted e precisamos ter a capacidade de definir o nome e o símbolo do construtor. O código final do contrato terá o seguinte aspecto:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract NFTCollection is ERC721, ERC721URIStorage, Ownable {
   using Counters for Counters.Counter;

   Counters.Counter private _tokenIdCounter;

   event TokenMinted(
       address indexed _contractAddress,
       address _recipient,
       uint256 _tokenId,
       string _tokenUri
   );

   constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {}

   function mint(address to, string memory _tokenUri) public onlyOwner {
       uint256 tokenId = _tokenIdCounter.current();
       _tokenIdCounter.increment();
       _safeMint(to, tokenId);
       _setTokenURI(tokenId, _tokenUri);
       emit TokenMinted(address(this), to, tokenId, _tokenUri);
   }

   // As seguintes funções são substituições requeridas pelo Solidity.

   function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
       super._burn(tokenId);
   }

   function tokenURI(
       uint256 tokenId
   ) public view override(ERC721, ERC721URIStorage) returns (string memory) {
       return super.tokenURI(tokenId);
   }
}
Enter fullscreen mode Exit fullscreen mode

Não se esqueça de instalar as dependências do openzeppelin:

yarn add @openzeppelin/contracts
Enter fullscreen mode Exit fullscreen mode

O próximo passo é criar uma fábrica, com a capacidade de criar coleções de nft e acompanhar os eventos de criação Factory.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "./NFTCollection.sol";

contract Factory {
   address[] public collections;
   uint256 public collectionsCount;
   event CollectionCreated(address indexed _contractAddress, string _name, string _symbol);

   function deployCollection(string memory _name, string memory _symbol) external {
       NFTCollection nftCollection = new NFTCollection(_name, _symbol);
       nftCollection.transferOwnership(msg.sender);
       collections.push(address(nftCollection));
       collectionsCount += 1;
       emit CollectionCreated(address(nftCollection), _name, _symbol);
   }
}
Enter fullscreen mode Exit fullscreen mode

Por favor, note que precisamos mudar o proprietário do contrato para o msg.sender, uma vez que ao carregar de uma fábrica, o proprietário é o endereço da fábrica.

nftCollection.transferOwnership(msg.sender);
Enter fullscreen mode Exit fullscreen mode

Vamos adicionar alguns testes.

tests/Factory.ts:

import { expect, assert } from "chai"
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"
import { ethers } from "hardhat"
import { NFTCollection } from "../typechain-types"
import { Factory } from "../typechain-types"

describe("Factory", function () {
   let factory: Factory
   let owner: SignerWithAddress
   let newContract: NFTCollection

   beforeEach(async () => {
       const Factory = await ethers.getContractFactory("Factory")
       factory = (await Factory.deploy()) as Factory
       await factory.deployed()

       const signers = await ethers.getSigners()
       owner = signers[0]
   })

   it("implanta um novo contrato NFTCollection", async () => {
       const name = "MyNFT"
       const symbol = "MNFT"
       await factory.connect(owner).deployCollection(name, symbol)
       const newContractAddress = await factory.collections(0)

       const NFTCollection = await ethers.getContractFactory("NFTCollection")
       const newContract = NFTCollection.attach(newContractAddress)
       const returnedName = await newContract.name()
       const returnedSymbol = await newContract.symbol()

       expect(returnedName).to.equal(name)
       expect(returnedSymbol).to.equal(symbol)
   })

   it("emite o evento CollectionCreated", async () => {
       const name = "MyNFT"
       const symbol = "MNFT"

       await new Promise<void>(async (resolve, reject) => {
           factory.once("CollectionCreated", async (_contractAddress, _name, _symbol) => {
               try {
                   assert.equal(_contractAddress, newContract.address)
                   assert.equal(_name, name)
                   assert.equal(_symbol, symbol)
                   resolve()
               } catch (e) {
                   reject(e)
               }
           })

           await factory.connect(owner).deployCollection(name, symbol)
           const newContractAddress = await factory.collections(0)
           const NFTCollection = await ethers.getContractFactory("NFTCollection")
           newContract = NFTCollection.attach(newContractAddress) as NFTCollection
       })
   })
})
Enter fullscreen mode Exit fullscreen mode

tests/NFTCollection.ts

import { expect, assert } from "chai"
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"
import { ethers } from "hardhat"
import { NFTCollection } from "../typechain-types"

describe("NFTCollection", () => {
   let nftCollection: NFTCollection
   let owner: SignerWithAddress
   let user: SignerWithAddress
   const tokenURI = "https://example.com/mytokenuri"

   beforeEach(async () => {
       const NFTCollection = await ethers.getContractFactory("NFTCollection")
       nftCollection = (await NFTCollection.deploy("MyNFT", "MNFT")) as NFTCollection
       await nftCollection.deployed()
       const signers = await ethers.getSigners()
       owner = signers[0]
       user = signers[1]
   })

   it("tenha um nome", async () => {
       expect(await nftCollection.name()).to.equal("MyNFT")
   })

   it("tenha um símbolo", async () => {
       expect(await nftCollection.symbol()).to.equal("MNFT")
   })

   it("retorna o URI do token correto", async function () {
       await nftCollection.connect(owner).mint(user.address, tokenURI)
       const tokenId = 0

       const returnedURI = await nftCollection.tokenURI(tokenId)
       expect(returnedURI).to.equal(tokenURI)
   })

   it("emite o evento TokenMinted", async () => {
       await new Promise<void>(async (resolve, reject) => {
           nftCollection.once(
               "TokenMinted",
               async (_contractAddress, _recipient, _tokenId, _tokenUri) => {
                   try {
                       assert.equal(_contractAddress, nftCollection.address)
                       assert.equal(_recipient, user.address)
                       assert.equal(_tokenId.toString(), "0")
                       assert.equal(_tokenUri, tokenURI)
                       resolve()
                   } catch (e) {
                       reject(e)
                   }
               }
           )

           await nftCollection.connect(owner).mint(user.address, tokenURI)
       })
   })
})
Enter fullscreen mode Exit fullscreen mode

Renomeie o arquivo .env.example para .env e preencha a chave privada, que pode ser retirada da metamask.

Também acrescente a .env RPC_URL e ETHERSCAN_API_KEY:

RPC_URL=https://eth-goerli.g.alchemy.com/v2/{your key}
ETHERSCAN_API_KEY="SUA_CHAVE"
Enter fullscreen mode Exit fullscreen mode

A seguir, implante na rede Goerli

hh deploy --network goerly
Enter fullscreen mode Exit fullscreen mode

Muito bom! No console, você verá o endereço do contrato da fábrica.

Repositórios:
https://github.com/AleksanderBorovoy/nft-collection-hardhat \
https://github.com/AleksanderBorovoy/nft-frontend

Esse artigo foi escrito por Aleksanderborovoydev e traduzido por Fátima Lima. O original pode ser lido aqui.

Top comments (0)