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.
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
A seguir, vá até a pasta e instale as dependências.
cd nft-collection && yarn install
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);
}
}
Não se esqueça de instalar as dependências do openzeppelin:
yarn add @openzeppelin/contracts
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);
}
}
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);
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
})
})
})
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)
})
})
})
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"
A seguir, implante na rede Goerli
hh deploy --network goerly
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)