Eu recentemente comecei a estudar Solidity novamente, revisando os detalhes, e estou escrevendo um "Guia Simples de Solidity" para iniciantes (programadores avançados podem procurar outros tutoriais), com atualizações semanais de 1-3 palestras.
Twitter: @0xAA_Science
Discord: WTF Academy
Todo o código e tutoriais são de código aberto no GitHub: github.com/AmazingAng/WTF-Solidity
Opensea é a maior plataforma de negociação de NFTs na Ethereum, com um volume total de negociação de mais de $30 bilhões. O Opensea cobra uma taxa de 2.5% em cada transação, o que significa que eles lucraram pelo menos $750 milhões com as transações dos usuários. Além disso, sua operação não é descentralizada e eles não estão planejando lançar uma moeda para compensar os usuários. Os jogadores de NFT estão insatisfeitos com o Opensea há muito tempo, e hoje vamos usar contratos inteligentes para construir uma exchange descentralizada de NFTs sem taxas: NFTSwap.
- Vendedor: a parte que vende o NFT, pode listar
list, cancelar a listagemrevoke, e alterar o preçoupdate. - Comprador: a parte que compra o NFT, pode comprar
purchase. - Pedido: uma ordem de NFT publicada pelo vendedor, uma série de tokens com o mesmo
tokenIdpode ter no máximo um pedido, contendo o preço de listagempricee as informações do proprietárioowner. Quando uma ordem é concluída ou cancelada, as informações são zeradas.
O contrato inclui 4 eventos, correspondentes à listagem list, à revogação revoke, à alteração de preço update e à compra purchase desses quatro comportamentos:
event List(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Purchase(address indexed buyer, address indexed nftAddr, uint256 indexed tokenId, uint256 price);
event Revoke(address indexed seller, address indexed nftAddr, uint256 indexed tokenId);
event Update(address indexed seller, address indexed nftAddr, uint256 indexed tokenId, uint256 newPrice);A ordem de NFT é abstraída como uma estrutura Order, contendo o preço de listagem price e as informações do proprietário owner. O mapeamento nftList registra a ordem correspondente ao contrato NFT e as informações do tokenId.
// Definição da estrutura da ordem
struct Order{
address owner;
uint256 price;
}
// Mapeamento da Ordem de NFT
mapping(address => mapping(uint256 => Order)) public nftList;No NFTSwap, os usuários usam ETH para comprar o NFT. Portanto, o contrato precisa implementar a função fallback() para receber ETH.
fallback() external payable{}A função de transferência segura ERC721 verifica se o contrato receptor implementou a função onERC721Received() e retorna o seletor correto. Após o usuário fazer um pedido, o NFT precisa ser enviado para o contrato NFTSwap. Portanto, o NFTSwap herda a interface IERC721Receiver e implementa a função onERC721Received():
contract NFTSwap is IERC721Receiver{
// Implementa a função onERC721Received do IERC721Receiver para poder receber tokens ERC721
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external override returns (bytes4){
return IERC721Receiver.onERC721Received.selector;
}O contrato implementa 4 funções relacionadas a transações:
- Listagem
list(): o vendedor cria o NFT e faz a ordem, acionando o eventoList. Os parâmetros são o endereço do contrato NFT_nftAddr, otokenIdcorrespondente do NFT_tokenIde o preço de listagem_price(nota: a unidade éwei). Após o sucesso, o NFT é transferido do vendedor para o contratoNFTSwap.
// Listagem: o vendedor lista o NFT para venda, o endereço do contrato é _nftAddr, o tokenId é _tokenId, o preço é em ethers (unidade em wei)
function list(address _nftAddr, uint256 _tokenId, uint256 _price) public{
IERC721 _nft = IERC721(_nftAddr); // Declara a variável da interface do contrato IERC721
require(_nft.getApproved(_tokenId) == address(this), "Necessita de Aprovação"); // O contrato precisa de autorização
require(_price > 0); // Preço maior que 0
Order storage _order = nftList[_nftAddr][_tokenId]; // Define o proprietário e o preço do NFT
_order.owner = msg.sender;
_order.price = _price;
// Transfere o NFT para o contrato
_nft.safeTransferFrom(msg.sender, address(this), _tokenId);
// Aciona o evento List
emit List(msg.sender, _nftAddr, _tokenId, _price);
}- Revogação
revoke(): o vendedor retira a listagem e aciona o eventoRevoke. Os parâmetros são o endereço do contrato NFT_nftAddre otokenIdcorrespondente do NFT_tokenId. Após o sucesso, o NFT é transferido de volta para o vendedor.
// Revogação: o vendedor retira a listagem
function revoke(address _nftAddr, uint256 _tokenId) public {
Order storage _order = nftList[_nftAddr][_tokenId]; // Obter a ordem
require(_order.owner == msg.sender, "Não é o Dono"); // Apenas o proprietário pode fazer isso
// Declara a variável da interface do contrato IERC721
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Pedido Inválido"); // O NFT está no contrato
// Transfere o NFT de volta para o vendedor
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
delete nftList[_nftAddr][_tokenId]; // Exclui a ordem
// Aciona o evento Revogar
emit Revoke(msg.sender, _nftAddr, _tokenId);
}- Atualização de preço
update(): o vendedor altera o preço da ordem do NFT e aciona o eventoUpdate. Os parâmetros são o endereço do contrato NFT_nftAddr, otokenIdcorrespondente do NFT_tokenIde o novo preço de listagem_newPrice(nota: a unidade éwei).
// Atualizar preço: o vendedor atualiza o preço da ordem do NFT
function update(address _nftAddr, uint256 _tokenId, uint256 _newPrice) public {
require(_newPrice > 0, "Preço Inválido"); // Preço do NFT maior que 0
Order storage _order = nftList[_nftAddr][_tokenId]; // Obter a ordem
require(_order.owner == msg.sender, "Não é o Dono"); // Apenas o proprietário pode fazer isso
// Declara a variável da interface do contrato IERC721
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Pedido Inválido"); // O NFT está no contrato
// Atualiza o preço do NFT
_order.price = _newPrice;
// Aciona o evento Atualizar
emit Update(msg.sender, _nftAddr, _tokenId, _newPrice);
}- Compra
purchase: o comprador paga comETHpara comprar o NFT listado e aciona o eventoPurchase. Os parâmetros são o endereço do contrato NFT_nftAddr, otokenIdcorrespondente do NFT_tokenId. Após o sucesso, o valor pago emETHé transferido para o vendedor e o NFT é transferido do contratoNFTSwappara o comprador.
// Compra: o comprador compra o NFT, fornecendo o endereço do contrato _nftAddr, tokenId _tokenId, e acompanhado por ETH
function purchase(address _nftAddr, uint256 _tokenId) payable public {
Order storage _order = nftList[_nftAddr][_tokenId]; // Obter a ordem
require(_order.price > 0, "Preço Inválido"); // Preço do NFT maior que 0
require(msg.value >= _order.price, "Aumente o preço"); // Preço de compra maior que o preço de listagem
// Declara a variável da interface do contrato IERC721
IERC721 _nft = IERC721(_nftAddr);
require(_nft.ownerOf(_tokenId) == address(this), "Pedido Inválido"); // O NFT está no contrato
// Transfere o NFT para o comprador
_nft.safeTransferFrom(address(this), msg.sender, _tokenId);
// Transfere o valor para o vendedor, e, se houver excesso, devolve o restante para o comprador
payable(_order.owner).transfer(_order.price);
payable(msg.sender).transfer(msg.value-_order.price);
delete nftList[_nftAddr][_tokenId]; // Exclui a ordem
// Aciona o evento Compra
emit Purchase(msg.sender, _nftAddr, _tokenId, _order.price);
}Siga o tutorial ERC721 para entender sobre NFTs e faça o deploy do contrato NFT WTFApe.
Mint o primeiro NFT para si mesmo, aqui estamos mintando os primeiros e segundos NFTs, com os tokenId sendo respectivamente 0 e 1.
No contrato WTFApe, confirme que agora você é o dono do NFT com tokenId igual a 0.
Siga os passos acima para mintar os NFTs com tokenId igual a 0 e 1.
Faça o deploy do contrato NFTSwap.
No contrato WTFApe, chame a função approve() para autorizar o tokenId do primeiro NFT a ser listado no contrato NFTSwap.
approve(address to, uint tokenId) tem 2 parâmetros:
to: o tokenId autorizado a ser listado para o endereço to, que neste caso é o endereço do contrato NFTSwap.
tokenId: o tokenId do NFT, neste caso o tokenId é 0.
Repita o processo para autorizar o tokenId igual a 1 para o contrato NFTSwap.
Chame a função list() do contrato NFTSwap para listar o NFT com tokenId igual a 0 no contrato NFTSwap, com o preço de 1 wei.
list(address _nftAddr, uint256 _tokenId, uint256 _price) tem 3 parâmetros:
_nftAddr: o endereço do contrato NFT, neste caso o endereço do contrato WTFApe.
_tokenId: o tokenId do NFT, neste caso o tokenId é 0.
_price: o preço do NFT, 1 wei neste caso.
Repita o processo para listar o NFT com tokenId igual a 1 no contrato NFTSwap, com o preço de 1 wei.
Chame a função nftList() no contrato NFTSwap para verificar os NFTs listados.
nftList: é um mapeamento de ordens de NFT, com a estrutura:
nftList[_nftAddr][_tokenId]: dando o _nftAddr e _tokenId, você obtém uma ordem de NFT.
Chame a função update() do contrato NFTSwap para atualizar o preço do NFT com tokenId igual a 0 para 77 wei.
update(address _nftAddr, uint256 _tokenId, uint256 _newPrice) tem 3 parâmetros:
_nftAddr: o endereço do contrato NFT, neste caso o endereço do contrato WTFApe.
_tokenId: o tokenId do NFT, neste caso o tokenId é 0.
_newPrice: o novo preço do NFT, 77 wei neste caso.
Após a atualização, verifique o preço atualizado chamando a função nftList.
Chame a função revoke() do contrato NFTSwap para deslistar o NFT.
No processo anterior, listamos dois NFTs com tokenId igual a 0 e 1. Desta vez, vamos deslistar o NFT com tokenId igual a 1.
revoke(address _nftAddr, uint256 _tokenId) tem 2 parâmetros:
_nftAddr: o endereço do contrato NFT, neste caso o endereço do contrato WTFApe.
_tokenId: o tokenId do NFT, neste caso o tokenId é 1.
Após chamar a função revoke(), verifique se o NFT foi deslistado chamando a função nftList. Para listá-lo novamente será necessário repetir os passos de autorização e listagem.
Nota: Após deslistar um NFT, você precisará autorizar e listar novamente para realizar a compra.
Troque de conta, e chame a função purchase() do contrato NFTSwap para comprar o NFT, fornecendo o endereço do contrato NFT, o tokenId e o valor de ETH a ser pago.
Deslistamos o NFT com tokenId igual a 1 anteriormente, agora vamos comprar o NFT com tokenId igual a 0.
purchase(address _nftAddr, uint256 _tokenId, uint256 _wei) tem 3 parâmetros:
_nftAddr: o endereço do contrato NFT, neste caso o endereço do contrato WTFApe.
_tokenId: o tokenId do NFT, neste caso o tokenId é 0.
_wei: a quantidade de ETH a ser paga, 77 wei neste caso.
Após a compra bem-sucedida, chame a função ownerOf() do contrato WTFApe para verificar a mudança do proprietário do NFT. Compra bem-sucedida!
Nesta palestra, construímos uma exchange descentralizada de NFTs sem taxas. O Opensea contribuiu muito para o desenvolvimento dos NFTs, mas suas desvantagens são evidentes: altas taxas, falta de recompensas em token para os usuários e um sistema de negociação suscetível a fraudes que podem resultar na perda de ativos dos usuários. Atualmente, novas plataformas de negociação de NFTs como Looksrare e dydx estão desafiando a posição do Opensea, e até o Uniswap está estudando novas exchanges de NFT. Acredito que em breve teremos acesso a exchanges de NFTs ainda melhores.











