WEB3DEV

Cover image for Transformando NFTs em carteiras
Panegali
Panegali

Posted on

Transformando NFTs em carteiras

Como usar o ERC-6551 para criar contas vinculadas a tokens não fungíveis

Introdução

Bem-vindo ao mundo dos tokens não fungíveis (NFTs), onde ativos digitais únicos estão conquistando o universo das blockchains. Com o aumento dos NFTs, vimos uma expansão massiva nas possibilidades do que pode ser alcançado nas redes blockchain. Um desenvolvimento empolgante é o conceito de um NFT atuando como uma carteira ou uma conta, uma ideia poderosa que abre uma infinidade de novos casos de uso e aplicações.

O ERC-721 é o padrão Ethereum para NFTs e, embora tenha servido bem à comunidade, suas limitações deram origem a novas extensões e conceitos. Uma dessas extensões é o ERC-6551, que traz a ideia de Contas Vinculadas a Tokens. Esta proposta, ainda em fase de rascunho no momento da escrita, visa dar a cada token ERC-721 sua própria conta de contrato inteligente, permitindo que os NFTs possuam ativos e interajam com aplicativos sem qualquer alteração nos contratos inteligentes ERC-721 existentes ou na infraestrutura.

Como funciona

Este guia apresenta o processo de transformar um NFT em uma carteira ou conta usando a proposta ERC-6551. Começamos por destacar os princípios fundamentais por trás do ERC-6551, que fornece uma interface padrão e um registro sem necessidade de permissão para criar contas vinculadas a tokens. Em seguida, exploramos as implementações de funções específicas, incluindo createAccount e account no Registro, e a interface IERC6551Account.

Depois, exploramos como implementar uma IERC6551Account em Solidity, criando um contrato inteligente onde um NFT age como uma carteira. Este contrato tem a capacidade de receber, manter e enviar tokens ERC-20, atuando essencialmente como uma carteira independente para cada NFT. Também compartilhamos um exemplo simplificado de um contrato inteligente que implementa essa funcionalidade, o que deve fornecer um ponto de partida sólido.

Lembre-se de que o código que compartilhamos é apenas para fins ilustrativos e, se você planeja implementá-lo em um ambiente de produção, é crucial ter o código auditado e testado minuciosamente.

A capacidade de transformar NFTs em carteiras ou contas, abre novas dimensões de possibilidades no mundo das blockchains. Isso abre o caminho para um NFT se tornar uma identidade digital única, capaz de possuir e gerenciar ativos na cadeia. Seja um avatar digital acumulando ativos de jogos, uma obra de arte mantendo seus royalties ou um cartão de membro registrando interações, os casos de uso potenciais são verdadeiramente fascinantes.

A Implementação

A tecnologia blockchain revolucionou a forma como abordamos a propriedade de ativos e transações. Entre esses avanços, os tokens não fungíveis (NFTs) - ativos únicos representados na blockchain - chamaram muito a atenção. No entanto, apesar de sua individualidade, os NFTs tradicionais apresentam uma limitação significativa: eles não podem possuir ativos ou interagir com aplicativos de forma independente. Aqui entra o ERC-6551, uma abordagem inovadora para estender as funcionalidades dos NFTs, equipando-os com suas próprias contas exclusivas, essencialmente transformando os NFTs em carteiras ou contas. Este artigo explora a implementação do ERC-6551, mostrando como ele pode tornar os NFTs mais versáteis e fornecendo trechos de código e melhores práticas ao longo do caminho.

Compreendendo o ERC-6551

A Proposta de Melhoria da Ethereum (EIP) ERC-6551 é um padrão recentemente introduzido que amplia as capacidades dos tokens ERC-721 (NFTs). Ela atribui a cada NFT uma conta de contrato inteligente única e determinística, permitindo que os NFTs possuam ativos e interajam com aplicativos sem exigir alterações nos contratos inteligentes ERC-721 existentes ou na infraestrutura¹​.

O sistema ERC-6551 consiste em dois componentes principais: um registro sem necessidade de permissão para implantar contas vinculadas a tokens e uma interface padrão para implementações¹​ de contas vinculadas a tokens​​. O registro implanta cada conta vinculada a token como um proxy mínimo ERC-1167 com dados constantes imutáveis anexados ao bytecode¹​​. O endereço de cada conta é determinístico e derivado de uma combinação única do endereço de implementação, endereço de contrato do token, ID do token, ID da cadeia EIP-155 e um salt opcional​¹​​.

Como Implementar o ERC-6551

O ERC-6551 é implementado por meio de um contrato de registro, que não tem necessidade de permissão, imutável e não possui proprietário. O registro serve como um ponto de entrada único para projetos que desejam utilizar contas¹​ vinculadas a tokens​​. O contrato de registro possui duas funções:

  • createAccount: implanta uma conta vinculada a token para um token ERC-721 dado um endereço implementation (de implementação).
  • account: uma função somente leitura que calcula o endereço da conta vinculada ao token para um token ERC-721 dado um endereço de implementação (implementation).

Aqui está a interface do contrato de registro:

interface IERC6551Registry {
   event AccountCreated(
       address account,
       address implementation,
       uint256 chainId,
       address tokenContract,
       uint256 tokenId,
       uint256 salt
   );

   function createAccount(
       address implementation,
       uint256 chainId,
       address tokenContract,
       uint256 tokenId,
       uint256 salt,
       bytes calldata initData
   ) external returns (address);

   function account(
       address implementation,
       uint256 chainId,
       address tokenContract,
       uint256 tokenId
   );
}
Enter fullscreen mode Exit fullscreen mode

A função createAccount pode ser usada para implantar uma nova conta vinculada a um token. Se a conta já tiver sido criada, ela retorna o endereço da conta sem chamar create2. Se initData não estiver vazia e a conta ainda não tiver sido criada, ela chama a conta com initData fornecido após a criação. A função account retorna o endereço calculado de uma conta vinculada a um token.

Construindo o Token Vinculado à Conta

Para desenvolver ainda mais o contrato inteligente, podemos começar estendendo as funcionalidades de nossa implementação IERC6551Account.

Primeiramente, vamos adicionar a funcionalidade para lidar com tokens ERC-20 que podem ser recebidos pelo NFT. Vamos adicionar uma função que permite que o NFT aprove outro endereço para gastar uma certa quantidade de um token ERC-20 específico:

interface IERC20 {
    function approve(address spender, uint256 amount) external returns (bool);
}

contract MyERC6551Account é IERC6551Account {
    // Outro código do contrato...

    function approveERC20(address tokenAddress, address spender, uint256 amount) external onlyOwner {
        IERC20 token = IERC20(tokenAddress);
        token.approve(spender, amount);
    }
}
Enter fullscreen mode Exit fullscreen mode

Aqui, onlyOwner é um modificador que garante que apenas o proprietário do NFT pode chamar esta função. IERC20 é uma interface simplificada para interagir com tokens ERC-20.

Em seguida, vamos adicionar a capacidade para nosso NFT interagir com um protocolo DeFi hipotético, como uma plataforma de empréstimo. Para simplificar, vamos adicionar uma função que permite ao NFT depositar uma certa quantidade de um token ERC-20 na plataforma:

interface ILendingPlatform {
    function deposit(address tokenAddress, uint256 amount) external;
}

contract MyERC6551Account é IERC6551Account {
    // Outro código do contrato...

    function depositToLendingPlatform(address platformAddress, address tokenAddress, uint256 amount) external onlyOwner {
        // Transfere a quantidade especificada de tokens para este contrato
        IERC20 token = IERC20(tokenAddress);
        require(token.transferFrom(msg.sender, address(this), amount), "Token transfer failed");

        // Aprova a plataforma de empréstimo para gastar os tokens
        token.approve(platformAddress, amount);

        // Deposita os tokens na plataforma de empréstimo
        ILendingPlatform platform = ILendingPlatform(platformAddress);
        platform.deposit(tokenAddress, amount);
    }
}
Enter fullscreen mode Exit fullscreen mode

Neste trecho de código, ILendingPlatform é uma interface simplificada para interagir com a plataforma de empréstimo hipotética. Primeiro, transferimos a quantidade especificada de tokens do proprietário do NFT para este contrato.Depois, aprovamos a plataforma de empréstimo para gastar esses tokens e, por fim, depositamos os tokens na plataforma de empréstimo.

Estes são apenas alguns exemplos do que pode ser alcançado com o ERC-6551. As possibilidades são verdadeiramente infinitas e dependem das necessidades específicas do seu projeto. Lembre-se de testar minuciosamente qualquer nova funcionalidade antes de implantá-la na rede principal para garantir que funcione como esperado e não tenha vulnerabilidades de segurança.

Transformando um NFT em uma Carteira

Para criar um contrato inteligente em que um NFT atue como uma carteira ou conta, você primeiro precisará implantar um contrato ERC-721. Esse contrato lidará com a cunhagem e transferência de seus NFTs.

Em seguida, você implementaria um contrato IERC6551Account que interage com o contrato ERC-721. Esse contrato teria a capacidade de receber, manter e enviar tokens ERC-20, essencialmente transformando cada NFT em uma carteira independente.

Aqui está um exemplo simplificado de como o contrato IERC6551Account poderia ser:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract NFTWallet is ERC721, ReentrancyGuard, Ownable {
    mapping(uint256 => address) private _wallets;

    constructor(string memory name, string memory symbol) ERC721(name, symbol) {}

    function walletOfToken(uint256 tokenId) public view returns (address) {
        return _wallets[tokenId];
    }

    function mint(address to) public onlyOwner {
        uint256 newTokenId = totalSupply() + 1;
        _mint(to, newTokenId);
        _wallets[newTokenId] = to;
    }

    function transferERC20(uint256 tokenId, address token, address to, uint256 amount) public nonReentrant {
        require(_wallets[tokenId] == msg.sender, "Apenas o proprietário da carteira pode transferir");
        IERC20(token).transferFrom(address(this), to, amount);
    }

    function depositERC20(uint256 tokenId, address token, uint256 amount) public nonReentrant {
        require(_wallets[tokenId] == msg.sender, "Apenas o proprietário da carteira pode depositar");
        IERC20(token).transferFrom(msg.sender, address(this), amount);
    }

    function balanceOfERC20(uint256 tokenId, address token) public view returns (uint256) {
        require(_wallets[tokenId] == msg.sender || ownerOf(tokenId) == msg.sender, "Não é o proprietário da carteira");
        return IERC20(token).balanceOf(address(this));
    }
}
Enter fullscreen mode Exit fullscreen mode

Neste contrato, cada NFT (identificado por seu tokenId) está associado a um endereço de carteira (_wallets[tokenId]). A função mint cunha um novo NFT e define o endereço da carteira como o endereço fornecido. A função transferERC20 permite que o proprietário do NFT transfira tokens ERC-20 da carteira associada ao NFT para outro endereço. A função depositERC20 permite que o proprietário do NFT deposite tokens ERC-20 de seu endereço pessoal na carteira associada ao NFT. A função balanceOfERC20 permite que o proprietário do NFT verifique o saldo de tokens ERC-20 na carteira associada ao NFT.

Lembre-se de que este contrato é simplificado para fins de clareza e não inclui a funcionalidade completa do padrão ERC-6551. Este código pode não ser totalmente seguro ou otimizado e deve ser testado e auditado minuciosamente antes de ser usado em um ambiente de produção.

Finalmente, lembre-se de que a implantação real desses contratos inteligentes exigirá taxas de gás, que são pagas em Ether. A quantidade de gás necessária dependerá da complexidade do seu contrato e do preço atual do gás na rede Ethereum.

Melhores Práticas

Ao implementar o ERC-6551, é crucial seguir as melhores práticas para garantir segurança e eficiência:

  • Garanta a Compatibilidade: o padrão ERC-6551 foi projetado para ser compatível com os tokens ERC-721 existentes. Sempre teste sua implementação com contratos ERC-721 existentes para garantir a compatibilidade.
  • Segurança: assim como qualquer contrato inteligente, a segurança é primordial. Certifique-se de que sua implementação seja segura e resistente a ataques comuns.
  • Segurança (continuação): vetores. Considere realizar uma auditoria de segurança antes da implantação. Mantenha sempre o controle de proprietário sobre seus tokens ERC-721 e suas contas associadas como prioridade.
  • Eficiência de Gás: certifique-se de que sua implementação seja eficiente em termos de gás. Lembre-se de que cada operação na blockchain Ethereum consome gás e contratos ineficientes podem levar a custos desnecessariamente altos.
  • Use os Endereços Corretos: certifique-se de que o endereço de implementação, o ID da cadeia, o endereço do contrato do token e o ID do token corretos sejam usados ao criar uma conta vinculada ao token. O uso de endereços incorretos ou inválidos pode levar a comportamentos inesperados ou à perda de controle sobre a conta.
  • Utilize o Salt: o valor opcional de salt na função de criação de conta permite criar endereços únicos para cada conta vinculada ao token, mesmo que outros parâmetros sejam idênticos. Use esse recurso para aumentar a exclusividade e segurança de cada conta.

Conclusão

O ERC-6551 é um avanço emocionante no ecossistema Ethereum, ampliando as capacidades dos tokens ERC-721 ao dar a eles suas contas únicas. Essa evolução abre novas possibilidades, incluindo NFTs que possuem ativos e interagem com aplicativos de forma independente. Este guia forneceu uma introdução ao ERC-6551 e sua implementação. No entanto, como qualquer nova tecnologia, ele deve ser usado com cautela e as melhores práticas devem ser seguidas para garantir a segurança e eficiência de seus contratos.


Artigo escrito por Javier Calderon Jr. Traduzido por Marcelo Panegali.

Top comments (0)