Passo a passo do contrato Vyper ERC-721
Introdução
O ERC-721 padrão é usado para manter a propriedade de tokens não fungíveis (NFT). ERC-20 os tokens se comportam como uma mercadoria, porque não há diferença entre os totens individuais. Em contraste com isso, ERC-721 tokens são projetados para ativos semelhantes, mas não idênticos, como diferentes cat cartoons(opens in a new tab) ou títulos de diferentes imóveis.
Neste artigo, vamos analisar o contrato ERC-721 de Ryuya Nakamura(opens in a new tab). Este contrato é escrito em Vyper(opens in a new tab), a Python-like linguagem de contrato projetada para tornar é mais difícil escrever código inseguro do que na solidez.
O Contrato
1# @dev Implementation of ERC-721 non-fungible token standard.2# @author Ryuya Nakamura (@nrryuya)3# Modified from: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vyCopiar
Comentários em Vyper, como em Python, começam com um hash (#
) e continuam até o final da linha. Comentários que incluem @<keyword>
são usados por NatSpec(opens in a new tab) para produzir documentação legível para humanos.
1from vyper.interfaces import ERC72123implements: ERC721Copiar
A interface ERC-721 é construída na linguagem Vyper. você pode ver o código definido aqui(opens in a new tab). A definição da interface é escrita em Python, em vez de Vyper, porque as interfaces são usadas não apenas dentro da blockchain, mas também ao enviar à blockchain uma transação de um cliente externo, que pode ser escrito em Python.
A primeira linha importa a ‘interface’, e o segundo especifica o que estamos incrementando aqui.
A interface ERC721Receiver
1# Interface for the contract called by safeTransferFrom()2interface ERC721Receiver:3 def onERC721Received(Copiar
ERC-721 suporta dois tipos de transferência:
transfere de
, que permite ao remetente especifique qualquer endereço de destino e coloca a responsabilidade para a transferência no remetente. Isso significa que você pode transferir para um endereço inválido, caso em que o NFT será perdido para sempre.safeTransferFrom
, que verifica se o endereço de destino é um contrato. Se for assim, o contrato ERC-721 pergunta ao contrato receptor se deseja receber o NFT.
Para responder transferência segura de
solicita um recebimento contrato deve implementar ERC721 recebedor
.
1 _operator: address,2 _from: address,Copiar
O endereço _de
é o proprietário atual do token. O endereço _operador
é aquele que solicitou a transferência (esses dois podem não ser o mesmo devido às provisões).
1 _tokenId: uint256,Copiar
ERC-721 token IDs está 256 bits. Normalmente, elas são criados por meio de uma execução de hash da descrição que o token representa.
1 _data: Bytes[1024]Copiar
O requerimento pode ter até 1024 bytes de dados do usuário.
1 ) -> bytes32: viewCopiar
Para evitar casos em que um contrato acidentalmente aceita uma transferência o valor de retorno não é um booleano, mas 256 bits com um valor específico.
Essa função é uma view
, o que significa que pode ler o estado da blockchain, mas não modificá-lo.
Eventos
Os eventos(opens in a new tab) são emitidos para informar usuários e servidores fora da blockchain de eventos. Observe que o conteúdo dos eventos não está disponível para contratos na blockchain.
1# @dev Emits when ownership of any NFT changes by any mechanism. This event emits when NFTs are2# created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any3# number of NFTs may be created and assigned without emitting Transfer. At the time of any4# transfer, the approved address for that NFT (if any) is reset to none.5# @param _from Sender of NFT (if address is zero address it indicates token creation).6# @param _to Receiver of NFT (if address is zero address it indicates token destruction).7# @param _tokenId The NFT that got transferred.8event Transfer:9 sender: indexed(address)10 receiver: indexed(address)11 tokenId: indexed(uint256)Exibir tudoCopiar
Isso é similar para o evento ERC-20 Transfer, exceto que informamos um tokenId
em vez de um valor. Ninguém possui o endereço zero, portanto, por convenção, o usamos para relatar a criação e a destruição de tokens.
1# @dev This emits when the approved address for an NFT is changed or reaffirmed. The zero2# address indicates there is no approved address. When a Transfer event emits, this also3# indicates that the approved address for that NFT (if any) is reset to none.4# @param _owner Owner of NFT.5# @param _approved Address that we are approving.6# @param _tokenId NFT which we are approving.7event Approval:8 owner: indexed(address)9 approved: indexed(address)10 tokenId: indexed(uint256)Exibir tudoCopiar
Uma aprovação ERC-721 é semelhante a uma permissão ERC-20. Um endereço específico é autorizado a transferir um determinado símbolo. Isso fornece um mecanismo para os contratos responderem quando aceitam um token. Os contratos não podem ouvir os eventos, portanto, se você apenas transferir o token para eles, eles não “saberão” disso. Desta forma, primeiro, o proprietário envia uma aprovação e, em seguida, envia uma solicitação ao contrato: “Aprovei para você transferir o token X, faça…”.
Esse é o designe escolhido por fazer o ERC-721 padrão semelhante ao padrão ERC-20. Como os tokens ERC-721 não são fungíveis, um contrato também pode identificar que obteve um token específico olhando a propriedade do token.
1# @dev This emits when an operator is enabled or disabled for an owner. The operator can manage2# all NFTs of the owner.3# @param _owner Owner of NFT.4# @param _operator Address to which we are setting operator rights.5# @param _approved Status of operator rights(true if operator rights are given and false if6# revoked).7event ApprovalForAll:8 owner: indexed(address)9 operator: indexed(address)10 approved: boolExibir tudoCopiar
Às vezes, é útil ter um operador que pode gerenciar todos os tokens da conta de um tipo específico (aqueles gerenciados por um contrato específico), semelhante a uma procuração. Por exemplo, eu posso querer dar tal poder a um contrato que verifica se Eu não tenho contatado ele por seis meses, e se assim for distribuo os meus bens aos meus herdeiros (se um deles o pedir, contrata não pode fazer nada sem ser chamado por uma transação). No ERC-20, podemos simplesmente atribuir uma provisão alta a um contrato de herança, mas isso não funciona para ERC-721, pois os tokens não são fungíveis. Isso é o equivalente.
O valor approved
nos informa se o evento é para uma aprovação ou a retirada de uma aprovação.
Variáveis de Estado
Essas variáveis contêm o estado atual dos tokens: os quais estão disponíveis e a quem os possui. A maioria delas são objetos HashMap
, mapeamentos unidirecionais que existem entre dois tipos(opens in a new tab).
1# @dev Mapping from NFT ID to the address that owns it.2idToOwner: HashMap[uint256, address]34# @dev Mapping from NFT ID to approved address.5idToApprovals: HashMap[uint256, address]Copiar
As identidades de usuários e contratos no Ethereum são representados por endereços de 160 bits. Essas duas variáveis mapeiam IDs de tokens para seus proprietários e aqueles aprovados a transferi-los (no máximo um para cada). No Ethereum, os dados não inicializados são sempre zero, pois, se não houver proprietário ou transferidor aprovado, o valor desse token será zero.
1# @dev Mapping from owner address to count of his tokens.2ownerToNFTokenCount: HashMap[address, uint256]Copiar
Essa variável possui a contagem de tokens para cada proprietário. Não há mapeamento de proprietários para tokens, então, a única forma de identificar os tokens que um proprietário específico possui é olhar para trás no histórico de eventos da blockchain e ver os eventos Transfer
apropriados. Podemos usar essa variável para saber quando temos todos os NFTs e não precisaremos mais olhar ainda mais no tempo.
Observe que esse algoritmo funciona apenas para interfaces do usuário e servidores externos. Código em execução na blockchain em si não pode ler eventos passados.
1# @dev Mapping from owner address to mapping of operator addresses.2ownerToOperators: HashMap[address, HashMap[address, bool]]Copiar
Uma conta pode ter mais de um único operador. Um simples HashMap
é insuficiente para mantê-los, pois cada chave gera um único valor. Em vez disso, você pode usar HashMap[address, bool]
como valor. Por padrão, o valor para cada endereço é False
, o que significa que ele não é um operador. Você pode definir valores como True
conforme necessário.
1# @dev Address of minter, who can mint a token2minter: addressCopiar
Novos tokens têm de ser criados de alguma forma. Neste contrato há uma única entidade que está autorizada a fazê-lo, o minter
. É provável que isso seja suficiente para um jogo, por exemplo. Para outros propósitos, pode ser necessário criar uma lógica de negócio mais complicada.
1# @dev Mapping of interface id to bool about whether or not it's supported2supportedInterfaces: HashMap[bytes32, bool]34# @dev ERC165 interface ID of ERC1655ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a767# @dev ERC165 interface ID of ERC7218ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cdCopiar
ERC-165(opens in a new tab) especifica um mecanismo para um contrato divulgar como aplicações podem se comunicar com ele, com os quais ERCs ele está em conformidade. Neste caso, o contrato está em conformidade com ERC-165 e ERC-721.
Funções
Estas são as funções que realmente implementam o ERC-721.
Construtor
1@external2def __init__():Copiar
No Vyper, assim como no Python, a função construtora é chamada __init__
.
1 """2 @dev Contract constructor.3 """Copiar
No Python e no Vyper, você também pode criar um comentário especificando uma string de múltiplas linhas (que começa e termina com """
), e não usá-lo de qualquer forma. Esses comentários também podem incluir NatSpec(opens in a new tab).
1 self.supportedInterfaces[ERC165_INTERFACE_ID] = True2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True3 self.minter = msg.senderCopiar
Para acessar variáveis de estado, você usa self.<variable name>
(novamente, o mesmo que em Python).
Exibir funções
São funções que não modificam o estado da blockchain e, por isso, podem ser executadas gratuitamente se chamadas externamente. Se as funções de exibição forem chamadas por um contrato, elas ainda têm de ser executadas em cada nó e, portanto, custam gás.
1@view2@externalCopiar
Essas palavras-chave anteriores a uma definição de função que começam com um sinal de (@
) são chamadas de decoradores. Elas especificam as circunstâncias em que uma função pode ser chamada.
@view
especifica que esta função é um modo de exibição.@external
especifica que essa função em particular pode ser chamada por transações e por outros contratos.
1def supportsInterface(_interfaceID: bytes32) -> bool:Copiar
Ao contrário do Python, o Vyper é uma linguagem de tipo estática(opens in a new tab). Você não pode declarar uma variável ou um parâmetro de função, sem identificar os tipos de dados(opens in a new tab). Neste caso, o parâmetro de entrada é bytes32
, um valor de 256 bits (256 bits é o tamanho da palavra nativa da Máquina Virtual do Ethereum). A saída é um booleano valor. Por convenção, os nomes dos parâmetros da função começam com um sublinhado (_
).
1 """2 @dev Interface identification is specified in ERC-165.3 @param _interfaceID Id of the interface4 """5 return self.supportedInterfaces[_interfaceID]Copiar
Retorne o valor do self.supportedInterfaces
HashMap, o qual é definido no construtor (__init__
).
1### VIEW FUNCTIONS ###Copiar
Estas são as funções de visualização que fornecem informações sobre os tokens disponíveis para usuários e outros contratos.
1@view2@external3def balanceOf(_owner: address) -> uint256:4 """5 @dev Returns the number of NFTs owned by `_owner`.6 Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.7 @param _owner Address for whom to query the balance.8 """9 assert _owner != ZERO_ADDRESSExibir tudoCopiar
Esta linha afirma(opens in a new tab) que _owner
não é zero. Se for zero, há um erro e a operação é anulada.
1 return self.ownerToNFTokenCount[_owner]23@view4@external5def ownerOf(_tokenId: uint256) -> address:6 """7 @dev Returns the address of the owner of the NFT.8 Throws if `_tokenId` is not a valid NFT.9 @param _tokenId The identifier for an NFT.10 """11 owner: address = self.idToOwner[_tokenId]12 # Throws if `_tokenId` is not a valid NFT13 assert owner != ZERO_ADDRESS14 return ownerExibir tudoCopiar
Na Máquina Virtual do Ethereum (EVM) qualquer armazenamento que não tenha um valor armazenado nele é zero. Se não houver token em _tokenId
, o valor de self.idToOwner[_tokenId]
é zero. Naquilo caso a função reverta.
1@view2@external3def getApproved(_tokenId: uint256) -> address:4 """5 @dev Get the approved address for a single NFT.6 Throws if `_tokenId` is not a valid NFT.7 @param _tokenId ID of the NFT to query the approval of.8 """9 # Throws if `_tokenId` is not a valid NFT10 assert self.idToOwner[_tokenId] != ZERO_ADDRESS11 return self.idToApprovals[_tokenId]Exibir tudoCopiar
Observe que getApproved
pode retornar zero. Se o token for válido, ele retorna self.idToApprovals[_tokenId]
. Se não houver aprovador, esse valor é zero.
1@view2@external3def isApprovedForAll(_owner: address, _operator: address) -> bool:4 """5 @dev Checks if `_operator` is an approved operator for `_owner`.6 @param _owner The address that owns the NFTs.7 @param _operator The address that acts on behalf of the owner.8 """9 return (self.ownerToOperators[_owner])[_operator]Exibir tudoCopiar
Esta função checa se _operator
tem permissão para gerenciar todos os tokens de _owner
neste contrato. Como pode haver vários operadores, este é um Hashmap de dois níveis.
Funções Auxiliares de Transferência
Essas funções implementam operações que fazem parte da transferência ou gerenciamento de tokens.
12### TRANSFER FUNCTION HELPERS ###34@view5@internalCopiar
Este decorador, @internal
, significa que a função é somente acessível de outras funções dentro do mesmo contrato. Por convenção, estes nomes de função também começam com um sublinhado (_
).
1def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool:2 """3 @dev Returns whether the given spender can transfer a given token ID4 @param spender address of the spender to query5 @param tokenId uint256 ID of the token to be transferred6 @return bool whether the msg.sender is approved for the given token ID,7 is an operator of the owner, or is the owner of the token8 """9 owner: address = self.idToOwner[_tokenId]10 spenderIsOwner: bool = owner == _spender11 spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId]12 spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender]13 return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAllExibir tudoCopiar
Há três maneiras na qual um endereço pode ser permitido a transferir um token:
- O endereço é o proprietário do token
- O endereço é aprovado a gastar o token
- O endereço é um operador do proprietário do token
A função acima pode ser uma view porque ela não muda o estado. Para reduzir custos operacionais, qualquer função que possa ser uma view, deve ser uma view.
1@internal2def _addTokenTo(_to: address, _tokenId: uint256):3 """4 @dev Add a NFT to a given address5 Throws if `_tokenId` is owned by someone.6 """7 # Throws if `_tokenId` is owned by someone8 assert self.idToOwner[_tokenId] == ZERO_ADDRESS9 # Change the owner10 self.idToOwner[_tokenId] = _to11 # Change count tracking12 self.ownerToNFTokenCount[_to] += 1131415@internal16def _removeTokenFrom(_from: address, _tokenId: uint256):17 """18 @dev Remove a NFT from a given address19 Throws if `_from` is not the current owner.20 """21 # Throws if `_from` is not the current owner22 assert self.idToOwner[_tokenId] == _from23 # Change the owner24 self.idToOwner[_tokenId] = ZERO_ADDRESS25 # Change count tracking26 self.ownerToNFTokenCount[_from] -= 1Exibir tudoCopiar
Quando há um problema com uma transferência, anulamos a chamada.
1@internal2def _clearApproval(_owner: address, _tokenId: uint256):3 """4 @dev Clear an approval of a given address5 Throws if `_owner` is not the current owner.6 """7 # Throws if `_owner` is not the current owner8 assert self.idToOwner[_tokenId] == _owner9 if self.idToApprovals[_tokenId] != ZERO_ADDRESS:10 # Reset approvals11 self.idToApprovals[_tokenId] = ZERO_ADDRESSExibir tudoCopiar
Altere o valor apenas se necessário. Variáveis de estado vivem no armazenamento. Escrever para o storage é uma das operações mais caras que a EVM (Máquina Virtual Ethereum) faz (em termos de gas). Portanto, é uma boa ideia minimizá-lo, mesmo escrevendo o valor existente tem um custo alto.
1@internal2def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address):3 """4 @dev Execute transfer of a NFT.5 Throws unless `msg.sender` is the current owner, an authorized operator, or the approved6 address for this NFT. (NOTE: `msg.sender` not allowed in private function so pass `_sender`.)7 Throws if `_to` is the zero address.8 Throws if `_from` is not the current owner.9 Throws if `_tokenId` is not a valid NFT.10 """Exibir tudoCopiar
Nós temos esta função interna porque há duas maneiras de transferir tokens (normal e segura), mas nós queremos somente uma única localização no código onde nós fazemos isso para facilitar auditoria.
1 # Check requirements2 assert self._isApprovedOrOwner(_sender, _tokenId)3 # Throws if `_to` is the zero address4 assert _to != ZERO_ADDRESS5 # Clear approval. Throws if `_from` is not the current owner6 self._clearApproval(_from, _tokenId)7 # Remove NFT. Throws if `_tokenId` is not a valid NFT8 self._removeTokenFrom(_from, _tokenId)9 # Add NFT10 self._addTokenTo(_to, _tokenId)11 # Log the transfer12 log Transfer(_from, _to, _tokenId)Exibir tudoCopiar
Para emitir um evento em Vyper você usa uma declaração de log
(veja aqui para mais detalhes(opens in a new tab)).
Funções de Transferência
12### TRANSFER FUNCTIONS ###34@external5def transferFrom(_from: address, _to: address, _tokenId: uint256):6 """7 @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved8 address for this NFT.9 Throws if `_from` is not the current owner.10 Throws if `_to` is the zero address.11 Throws if `_tokenId` is not a valid NFT.12 @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else13 they maybe be permanently lost.14 @param _from The current owner of the NFT.15 @param _to The new owner.16 @param _tokenId The NFT to transfer.17 """18 self._transferFrom(_from, _to, _tokenId, msg.sender)Exibir tudoCopiar
Esta função deixa você transferir para um endereço arbitrário. A não ser que o endereço é um usuário, ou um contrato que sabe como transferir tokens, qualquer token que você transferir ficará preso no endereço e inútil.
1@external2def safeTransferFrom(3 _from: address,4 _to: address,5 _tokenId: uint256,6 _data: Bytes[1024]=b""7 ):8 """9 @dev Transfers the ownership of an NFT from one address to another address.10 Throws unless `msg.sender` is the current owner, an authorized operator, or the11 approved address for this NFT.12 Throws if `_from` is not the current owner.13 Throws if `_to` is the zero address.14 Throws if `_tokenId` is not a valid NFT.15 If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if16 the return value is not `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.17 NOTE: bytes4 is represented by bytes32 with padding18 @param _from The current owner of the NFT.19 @param _to The new owner.20 @param _tokenId The NFT to transfer.21 @param _data Additional data with no specified format, sent in call to `_to`.22 """23 self._transferFrom(_from, _to, _tokenId, msg.sender)Exibir tudoCopiar
Tudo bem fazer a transferência primeiro, porque se der um problema, vamos revertê-la de qualquer maneira, a fim de anular tudo o que foi feito durante a chamada.
1 if _to.is_contract: # check if `_to` is a contract addressCopiar
Primeiro cheque para ver se o endereço é um contrato (se ele tem código). Se não, assuma que ele é um endereço de usuário e o usuário será capaz de usar o token ou transferi-lo. Mas não deixe que isso engane você com uma falsa sensação de segurança. Você pode perder tokens, mesmo com safeTransferFrom
, se você transferi-los para um endereço que ninguém conhece a chave privada.
1 returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)Copiar
Chame o contrato-alvo para ver se ele pode receber tokens ERC-721.
1 # Throws if transfer destination is a contract which does not implement 'onERC721Received'2 assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32)Copiar
Se o destino é um contrato, mas um que não aceita tokens ERC-721 (ou que decide não aceitar esta transferência em particular), reverta.
1@external2def approve(_approved: address, _tokenId: uint256):3 """4 @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address.5 Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.6 Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP)7 Throws if `_approved` is the current owner. (NOTE: This is not written the EIP)8 @param _approved Address to be approved for the given NFT ID.9 @param _tokenId ID of the token to be approved.10 """11 owner: address = self.idToOwner[_tokenId]12 # Throws if `_tokenId` is not a valid NFT13 assert owner != ZERO_ADDRESS14 # Throws if `_approved` is the current owner15 assert _approved != ownerExibir tudoCopiar
Por convenção, se você não quiser ter um aprovador, designe o endereço zero, não você mesmo.
1 # Check requirements2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender3 senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]4 assert (senderIsOwner or senderIsApprovedForAll)Copiar
Para configurar um aprovador você pode ou ser o proprietário, ou um operador autorizado pelo proprietário.
1 # Set the approval2 self.idToApprovals[_tokenId] = _approved3 log Approval(owner, _approved, _tokenId)456@external7def setApprovalForAll(_operator: address, _approved: bool):8 """9 @dev Enables or disables approval for a third party ("operator") to manage all of10 `msg.sender`'s assets. It also emits the ApprovalForAll event.11 Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP)12 @notice This works even if sender doesn't own any tokens at the time.13 @param _operator Address to add to the set of authorized operators.14 @param _approved True if the operators is approved, false to revoke approval.15 """16 # Throws if `_operator` is the `msg.sender`17 assert _operator != msg.sender18 self.ownerToOperators[msg.sender][_operator] = _approved19 log ApprovalForAll(msg.sender, _operator, _approved)Exibir tudoCopiar
Cunhar novos tokens e destruir os existentes
A conta que criou o contrato é o minter
, o superusuário autorizado a cunhar novos NFTs. No entanto, mesmo isso não é autorizado para queimar tokens existentes. Somente o proprietário, ou uma entidade autorizada pelo proprietário, podem fazer isso.
1### MINT & BURN FUNCTIONS ###23@external4def mint(_to: address, _tokenId: uint256) -> bool:Copiar
Esta função sempre retorna True
, porque se a operação falhar, ela é revertida.
1 """2 @dev Function to mint tokens3 Throws if `msg.sender` is not the minter.4 Throws if `_to` is zero address.5 Throws if `_tokenId` is owned by someone.6 @param _to The address that will receive the minted tokens.7 @param _tokenId The token id to mint.8 @return A boolean that indicates if the operation was successful.9 """10 # Throws if `msg.sender` is not the minter11 assert msg.sender == self.minterExibir tudoCopiar
Somente o minter (a conta que criou o contrato ERC-721) pode cunhar novos tokens. Isso pode ser um problema no futuro se você quiser mudar a identidade do minter. Em um contrato de produção, provavelmente seria desejável ter uma função que permita ao minter transferir privilégios de minter para uma outra pessoa.
1 # Throws if `_to` is zero address2 assert _to != ZERO_ADDRESS3 # Add NFT. Throws if `_tokenId` is owned by someone4 self._addTokenTo(_to, _tokenId)5 log Transfer(ZERO_ADDRESS, _to, _tokenId)6 return TrueCopiar
Por convenção, a cunhagem de novos tokens conta como uma transferência do endereço zero.
12@external3def burn(_tokenId: uint256):4 """5 @dev Burns a specific ERC721 token.6 Throws unless `msg.sender` is the current owner, an authorized operator, or the approved7 address for this NFT.8 Throws if `_tokenId` is not a valid NFT.9 @param _tokenId uint256 id of the ERC721 token to be burned.10 """11 # Check requirements12 assert self._isApprovedOrOwner(msg.sender, _tokenId)13 owner: address = self.idToOwner[_tokenId]14 # Throws if `_tokenId` is not a valid NFT15 assert owner != ZERO_ADDRESS16 self._clearApproval(owner, _tokenId)17 self._removeTokenFrom(owner, _tokenId)18 log Transfer(owner, ZERO_ADDRESS, _tokenId)Exibir tudoCopiar
Qualquer pessoa autorizada a transferir um token, tem permissão para queimá-lo. Enquanto uma queima aparece equivalente à transferência para o endereço zero, o endereço zero não recebe de verdade o token. Isso permite-nos liberar todo o armazenamento usado pelo token, o que pode reduzir o custo de gás da transação.
Usando este contrato
Ao contrário do Solidity, o Vyper não tem herança. Esta é uma escolha de design deliberada para tornar o código mais claro e, com isso, mais fácil de proteger. Portanto, para criar seu próprio contrato Vyper ERC-721, você usa este contrato(opens in a new tab) e o modifica para implementar a lógica comercial que você desejar.
Conclusão
Recapitulando, aqui estão algumas das ideias mais importantes neste contrato:
- Para receber os tokens ERC-721 com uma transferência segura, os contratos têm de implementar a interface
ERC721Receiver
. - Mesmo que você use a transferência segura, os tokens ainda podem ficar presos se você os enviar para um endereço cuja chave privada.
- Quando há um problema com uma operação, é uma boa ideia fazer o
revert
da chamada, em vez de apenas retornar um valor de falha. - Os tokens ERC-721 existem quando eles têm um proprietário.
- Existem três formas de ser autorizado a transferir um NFT. Você pode ser o proprietário, ser aprovado para um token específico, ou ser um operador para todos os tokens do proprietário.
- Eventos passados são visíveis apenas fora da blockchain. O código executando dentro da blockchain não pode visualizá-los.
Agora, você está pronto para implementar contratos Vyper seguros.
Última edição: @nhsz(opens in a new tab), 15 de agosto de 2023