WEB3DEV

Cover image for Cunhagem Preguiçosa
Fatima Lima
Fatima Lima

Posted on

Cunhagem Preguiçosa

A cunhagem de um NFT em uma mainnet de blockchain geralmente custa algum dinheiro, pois a gravação de dados na blockchain exige uma taxa (geralmente chamada de gas) para pagar pela computação e pelo armazenamento. Isso pode ser um obstáculo para os criadores de NFTs, especialmente para os novatos, que talvez não queiram investir muito dinheiro antes de saber se seu trabalho será vendido.

Usando algumas técnicas avançadas, é possível adiar o custo da cunhagem de uma NFT até o momento em que ela é vendida ao seu primeiro comprador. As taxas de gas para a cunhagem são incluídas na mesma transação que atribui o NFT ao comprador, de modo que o criador do NFT nunca precisa pagar para cunhar. Em vez disso, uma parte do preço de compra simplesmente vai para cobrir o gas adicional necessário para criar o registro inicial do NFT.

Essa cunhagem "bem na hora” da compra é geralmente chamada de cunhagem preguiçosa (lazy minting) e tem sido adotada por mercados como a OpenSea para reduzir a barreira de entrada para os criadores de NFTs, possibilitando a criação de NFTs sem nenhum custo inicial.

Este guia mostrará um exemplo de cunhagem preguiçosa na Ethereum, usando algumas bibliotecas auxiliares e contratos básicos do OpenZeppelin. Se você é novo na cunhagem de NFTs em geral, nosso tutorial sobre o serviço de cunhagem é um ótimo lugar para começar a avançar no básico.

Ao longo do guia, faremos referência a um projeto de exemplo, que reside no diretório Repositório de exemplos da NFT School. Se quiser se aprofundar, clone o repositório e abra o exemplo em seu editor favorito:

git clone https://github.com/ipfs-shipyard/nft-school-examples
cd nft-school-examples/lazy-minting
code . # ou o editor que você preferir
Enter fullscreen mode Exit fullscreen mode

Como ele funciona

A premissa básica da cunhagem preguiçosa é que, em vez de criar um NFT diretamente chamando uma função de contrato, o criador do NFT prepara uma assinatura criptográfica de alguns dados usando a chave privada de sua conta Ethereum.

Os dados assinados funcionam como um "voucher" ou bilhete que pode ser trocado por um NFT. O voucher contém todas as informações que serão incluídas no NFT real e, opcionalmente, pode conter dados adicionais que não são registrados na blockchain, como veremos daqui a pouco quando falarmos sobre preços. A assinatura comprova que o criador do NFT autorizou a criação do NFT específico descrito no voucher.

Quando um comprador deseja adquirir o NFT, ele chama uma função redeem para resgatar o voucher assinado. Se a assinatura for válida e pertencer a uma conta autorizada a cunhar NFTs, um novo token será criado com base no comprovante e transferido para o comprador.

Em nosso exemplo, estamos usando uma struct Solidity para representar nosso voucher:

struct NFTVoucher {
  uint256 tokenId;
  uint256 minPrice;
  string uri;
  bytes signature;
}
Enter fullscreen mode Exit fullscreen mode

O voucher contém duas informações que serão registradas na blockchain: o tokenId exclusivo e o uri para os metadados do token. O minPrice não é gravado, mas é usado em nossa função redeem para permitir que o criador defina um preço de compra. Se o minPrice for maior que zero, o comprador precisará enviar pelo menos essa quantidade de Ether quando chamar redeem.

O campo signature em nossa struct contém uma assinatura preparada pelo criador do NFT, conforme descrito na próxima seção.

TIP

Definir um preço de compra dentro do voucher nem sempre é necessário, mas você provavelmente precisará de algum tipo de condição. Caso contrário, qualquer pessoa que tenha o voucher poderá reivindicar o NFT apenas pelo custo do gas!

Por exemplo, se você estiver fazendo airdrop de NFTs em contas específicas e souber antecipadamente os endereços dos destinatários, seu voucher poderá incluir um campo address recipient ao invés de um minPrice e sua função redeem pode se certificar de que msg.sender == voucher.recipient.

Criando um voucher assinado

O uso de assinaturas para autorização pode ser complicado, pois uma terceira parte traiçoeira poderia pegar alguns dados que foram assinados em um contexto e apresentá-los em outro. Por exemplo, eles podem pegar uma assinatura que autoriza a criação de um NFT na rede de teste Ropsten e apresentá-la a um contrato implantado na rede principal. A menos que os dados que estão sendo assinados contenham algumas informações de contexto, esse tipo de "ataque de repetição" é bastante simples de executar e difícil de ser defendido.

Para resolver essas questões e também proporcionar uma melhor experiência ao usuário, ao assinar mensagens, a comunidade Ethereum desenvolveu o EIP-712, um padrão para assinatura de dados tipados e estruturados. As assinaturas criadas com a EIP-712 são "vinculadas" a uma instância específica de um contrato inteligente executado em uma rede específica. Elas também contêm informações de tipo, de modo que ferramentas como a MetaMask podem apresentar ao usuário mais detalhes sobre os dados que estão sendo assinados, em vez de uma cadeia opaca de caracteres hexadecimais.

Nosso exemplo usa uma classe JavaScript chamada LazyMinter para preparar comprovantes assinados usando EIP-712. Como as assinaturas estão vinculadas a uma instância de contrato específica, você precisa fornecer o endereço do contrato implantado e um Signer ethers.js para a chave privada do criador do NFT:

const lazyminter = new LazyMinter({ myDeployedContract.address, signerForMinterAccount })
Enter fullscreen mode Exit fullscreen mode

Aqui está o método principal createVoucher que cria vouchers de NFT assinados:

 async createVoucher(tokenId, uri, minPrice = 0) {
    const voucher = { tokenId, uri, minPrice }
    const domain = await this._signingDomain()
    const types = {
      NFTVoucher: [
        {name: "tokenId", type: "uint256"},
        {name: "minPrice", type: "uint256"},
        {name: "uri", type: "string"},  
      ]
    }
    const signature = await this.signer._signTypedData(domain, types, voucher)
    return {
      ...voucher,
      signature,
    }
  }
Enter fullscreen mode Exit fullscreen mode

Primeiro, preparamos nosso objeto voucher não assinado e obtemos o domínio de assinatura a ser usado para o EIP-712. O objeto types contém as informações de tipo para os campos do nosso NFTVoucher (excluindo a própria assinatura).

Para criar a assinatura, chamamos o método _signTypedData em nosso objeto Signer, passando pelo domínio, definição de tipo e objeto voucher não assinado.

Por fim, retornamos o objeto voucher completo com a assinatura incluída, que pode ser resgatada em nosso contrato inteligente.

AVISO

O método _signTypedData será renomeado para signTypedData na versão futura do ethers.js! Veja os documentos ethers para obter mais informações.

Resgatando um voucher on-chain

Para que a cunhagem preguiçosa funcione, precisamos de uma função de contrato inteligente que o comprador de NFT possa chamar para cunhar o NFT desejado e atribuí-lo à sua conta; tudo em uma única transação. A nossa função é chamada redeem:

 function redeem(address redeemer, NFTVoucher calldata voucher) public payable returns (uint256) {
    //Assegura que a assinatura é válida e obtém o endereço do signatário
    address signer = _verify(voucher);

    // assegura que o signatário esteja autorizado a cunhar NFTs    require(hasRole(MINTER_ROLE, signer), "Signature invalid or unauthorized");

    // assegura que o comprador esteja pagando o suficiente para cobrir os custos do comprador
    require(msg.value >= voucher.minPrice, "Insufficient funds to redeem");

    // primeiro atribui o token ao signatário, para estabelecer a procedência on-chain
    _mint(signer, voucher.tokenId);
    _setTokenURI(voucher.tokenId, voucher.uri);

    // transfere o token para o comprador
    _transfer(signer, redeemer, voucher.tokenId);

    // registra o pagamento no saldo de retirada do signatário
    pendingWithdrawals[signer] += msg.value;

    return voucher.tokenId;
  }
Enter fullscreen mode Exit fullscreen mode

Primeiro, chamamos uma função auxiliar _verify, que retorna o endereço da conta que preparou a assinatura ou reverte a transação se a assinatura for inválida.

Uma vez que tivermos o endereço do signatário, verificamos se ele está autorizado a criar NFTs usando a função hasRole do

Contrato AccessControl role-based do OpenZeppelin.

Também nos certificamos de que o comprador tenha enviado ETH suficiente para cobrir o minPrice. Em caso afirmativo, podemos criar um novo token com base nas informações do voucher e transferi-lo para a conta redeemer.

Por fim, colocamos o pagamento em um mapeamento chamado pendingWithdrawals, para que o criador do NFT possa retirar seu ETH mais tarde.

É isso! Se você estiver curioso sobre a verificação da assinatura, consulte a fonte do contrato e os documentos para o contrato base do OpenZeppelin EIP-712.

Conclusão

A cunhagem preguiçosa é uma técnica poderosa que pode permitir que os criadores emitam novos NFTs sem custo inicial.

Embora tenhamos demonstrado a técnica principal aqui, uma plataforma de produção precisará de muito mais! Por exemplo, você provavelmente precisará de um aplicativo para que os criadores de NFTs emitam comprovantes assinados e provavelmente desejará algum tipo de sistema de back-end para controlar todos os NFTs "não cunhados" que estão esperando para serem resgatados.

Divirta-se criando e nos informe se houver algo que você gostaria de ver nesta página e que não tenha sido abordado!

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

Latest comments (0)