Una explicación del contrato ERC-721
Introducción
La norma ERC-721 sirve para mantener la propiedad de los tókenes no fungibles (o NFT). Los tókenes ERC-20 actúan como mercancía, porque no hay diferencia entre tókenes individuales. En contraste, los tókenes ERC-721 están diseñados para activos similares, pero no identicos, tales como catcartoons(opens in a new tab) o títulos a diferentes piezas de bienes inmuebles.
En este artículo analizaremos el contrato ERC-721 de Ryuya Nakamura(opens in a new tab). Este contrato está escrito en Vyper(opens in a new tab), un lenguaje de contrato similar a Python diseñado para hacer más difícil escribir código inseguro que en Solidity.
El 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
Comentarios en Vyper, como en Python, empiezan con un hash (#
) y continúan hasta el final de la línea. Comentarios que incluyen @<keyword>
los usan NatSpec(opens in a new tab) para producir documentación legible.
1from vyper.interfaces import ERC72123implements: ERC721Copiar
La interfaz ERC-721 está integrada en el lenguaje Vyper. Puede ver la definición del código aquí(opens in a new tab). La definición de la interfaz está escrita en Python, en lugar de Vyper, porque las interfaces se utilizan no solo dentro de la cadena de bloques, sino que también al enviar a la cadena de bloques una transacción desde un cliente externo, que puede estar escrito en Python.
La primera línea importa la interfaz, y la segunda especifica que la estamos implementando aquí.
La interfaz de receptor ERC721
1# Interface for the contract called by safeTransferFrom()2interface ERC721Receiver:3 def onERC721Received(Copiar
ERC-721 soporta dos tipos de transferencia:
transferFrom
, que permite al remitente especificar cualquier dirección de destino y responsabiliza al remitente de la transferencia. Esto significa que puede transferir a una dirección no válida, en cuyo caso el NFT se pierde para siempre.safeTransferFrom
, que comprueba si la dirección de destino es un contrato. Si es así, el contrato ERC-721 le pregunta al contrato receptor si quiere recibir el NFT.
Para responder a safeTransferFrom
, se solicitará un contrato receptor que implemente ERC721Receiver
.
1 _operator: address,2 _from: address,Copiar
La dirección _from
es el dueño actual del token. La dirección _operador
es la que solicitó la transferencia (estos dos no pueden ser los mismos, debido a permisos).
1 _tokenId: uint256,Copiar
Las ID del token ERC-721 son de 256 bits. Normalmente se crean al cifrar una descripción de lo que sea representara el token.
1 _data: Bytes[1024]Copiar
La solicitud puede tener hasta 1.024 bytes de datos de usuario.
1 ) -> bytes32: viewCopiar
Para prevenir casos en los que un contrato acepte accidentalmente una transferencia, el valor de retorno no es un booleano, si no 256 bits con un valor específico.
Esta función es una view
, lo que significa que puede leer el estado de la cadena de bloques, pero no modificarla.
Events
Los eventos(opens in a new tab) se emiten para informar a los usuarios y servidores fuera de la cadena de bloques de eventos. Tenga en cuenta que el contenido de los eventos no está disponible para los contratos en la cadena de bloques.
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)Mostrar todoCopiar
Esto es similar a una transferencia de ERC-20, excepto que informamos de un tokenId
en lugar de una cantidad. Nadie tiene la dirección cero, así que convencionalmente la utilizamos para informar de la creación y destrucción de tókenes .
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)Mostrar todoCopiar
La aprobación de una norma ERC-721 es similar a una autorización para el ERC. Se permite una dirección específica para transferir un token específico. Esto da un mecanismo para que los contratos respondan cuando aceptan un token. Los contratos no pueden escuchar eventos, así que si solo transfiere el token a ellos, estos no lo sabrán. De esta manera el propietario de envía primero una aprobación y luego envía una solicitud al contrato: «He aprobado la transferencia del token X, hágalo por favor...».
Esta es una opción de diseño para hacer que la norma ERC-721 sea similar la norma ERC-20. Debido a que los tókenes ERC-721 no son fungibles, un contrato también puede identificar que obtuvo un token específico mirando la propiedad del 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: boolMostrar todoCopiar
A veces es útil tener un operator que pueda administrar todos los tókenes de una cuenta de un tipo específico (aquellos que administra un contrato específico), similar a un poder notarial. Por ejemplo, querría dar tal poder a un contrato que comprueba si no me he puesto en contacto con él durante seis meses. Y si así lo desea, distribuye mis activos a mis herederos (si uno de ellos lo solicita, los contratos no pueden hacer nada sin ser invocados por una transacción). En ERC-20 sólo podemos dar una alta asignación a un contrato de herencia, pero eso no funciona para ERC-721 porque los tókenes no son fungibles. Este es el equivalente.
El valor approved
nos dice si el evento es para una aprobación o la retirada de una aprobación.
Variables de estado
Estas variables contienen el estado actual de los tókenes: cuáles están disponibles y quién los posee. La mayoría de estos son objetos de HashMap
, , asignaciones unidireccionales que existen entre dos 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
Las identidades del usuario y del contrato en Ethereum vienen representadas por direcciones de 160-bits. Estas dos variables mapean desde identificadores de tókenes a sus propietarios y aquellos aprobados para transferirlos (máximo uno para cada uno). En Ethereum, los datos no inicializados siempre son cero, así que si no hay ningún propietario o transferidor aprobado, el valor para ese token es cero.
1# @dev Mapping from owner address to count of his tokens.2ownerToNFTokenCount: HashMap[address, uint256]Copiar
Esta variable tiene el recuento de tókenes para cada propietario. No hay mapeo de propietarios a tókenes, así que la única manera de identificar los tókenes que posee un propietario específico es mirar el historial de eventos de la cadena de bloques y ver los eventos apropiados de Transfer
. Podemos usar esta variable para saber cuando tenemos todos los NFT y no necesitamos mirar aún más a tiempo.
Tenga en cuenta que este algoritmo sólo funciona para interfaces de usuario y servidores externos. El código en ejecución en la cadena de bloques no puede leer eventos pasados.
1# @dev Mapping from owner address to mapping of operator addresses.2ownerToOperators: HashMap[address, HashMap[address, bool]]Copiar
Una cuenta puede tener más de un operador único. Un simple HashMap
es insuficiente para llevar un seguimiento de ellos, porque cada clave conduce a un único valor. En su lugar, puede utilizar HashMap[address, bool]
como el valor. Por defecto, el valor para cada dirección es False
, lo que significa que no es un operador. Puede establecer valores a True
según sea necesario.
1# @dev Address of minter, who can mint a token2minter: addressCopiar
Hay que crear nuevos tókenes de alguna manera. En este contrato hay una única entidad que puede hacerlo, el minter
. Es probable que esto sea suficiente para un juego, por ejemplo. Para otros propósitos, podría ser necesario crear una lógica de negocio más 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 un mecanismo para un contrato para divulgar cómo las aplicaciones pueden comunicarse con él, con qué ERC concuerda. En este caso, el contrato se ajusta a ERC-165 y ERC-721.
Funciones
Estas son las funciones que implementan ERC-721.
El constructor
1@external2def __init__():Copiar
En Vyper, como en Python, la función constructora se llama __init__
.
1 """2 @dev Contract constructor.3 """Copiar
En Python, y en Vyper, también puedes crear un comentario especificando una cadena multilínea (que comienza y termina en """
), y no usarla de ninguna manera. Estos comentarios también pueden 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 acceder a variables de estado se usa self.<nombre de variable>
(otra vez, igual que en Python).
Funciones View
Estas son funciones que no modifican el estado de la cadena de bloques y que, por lo tanto, pueden ejecutarse libremente si se invocan externamente. Si las funciones de vista (View) se invocan mediante un contrato y todavía tienen que ejecutarse en cada nodo y por lo tanto cuestan gas.
1@view2@externalCopiar
Estas palabras clave antes de una definición de función que empiezan con un signo en la pantalla (@
) se llaman decoraciones. Y especifican las circunstancias en las que se puede activar una función.
@view
especifica que esta función es una vista.@external
especifica que esta función en concreto puede activarse por transacciones y por otros contratos.
1def supportsInterface(_interfaceID: bytes32) -> bool:Copiar
A diferencia de Python, Vyper es un lenguaje tipeado estático(opens in a new tab). No se puede declarar una variable o un parámetro de función sin identificar el tipo de datos(opens in a new tab). En este caso, el parámetro de entrada es bytes32
, un valor de 256 bits (que es el tamaño de la palabra nativa de la máquina virtual de Ethereum). La salida es un valor booleano. Por costumbre, los nombres de los parámetros de función comienzan por un guión bajo (_
).
1 """2 @dev Interface identification is specified in ERC-165.3 @param _interfaceID Id of the interface4 """5 return self.supportedInterfaces[_interfaceID]Copiar
Devuelve el valor del HashMap self.supportedInterfaces
, que se establece en el constructor (__init__
).
1### VIEW FUNCTIONS ###Copiar
Estas son las funciones de vista que hacen que la información sobre los tókenes esté disponible para los usuarios y otros 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_ADDRESSMostrar todoCopiar
Esta línea verifica(opens in a new tab) que _owner
no es cero. Si lo es, hay un error y el funcionamiento se revierte.
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 ownerMostrar todoCopiar
En la máquina virtual de Ethereum (EVM) cualquier almacenamiento que no tenga un valor almacenado en ella es cero. Si no hay ningún token en _tokenId
, entonces el valor de self.idToOwner[_tokenId]
es cero. En ese caso la función se revierte.
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]Mostrar todoCopiar
Tenga en cuenta que getApproved
puede dar cero como valor. Si el token es válido, aparece self.idToApprovals[_tokenId]
. Si no hay aprobador, ese valor es cero.
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]Mostrar todoCopiar
Esta función verifica si _operator
tiene permitido administrar todos los tókenes de _owner
de este contrato. Debido a que puede haber múltiples operadores, se trata de un HashMap de dos niveles.
Funciones auxiliares de transferencia
Estas funciones implementan operaciones que son parte de la transferencia o la gestión de tókenes.
12### TRANSFER FUNCTION HELPERS ###34@view5@internalCopiar
Esta decoración @internal
significa que solo se puede accedeer a la función desde otras funciones dentro del mismo contrato. Por costumbre, los nombres de los parámetros de función comienzan por un guión bajo (_
).
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 spenderIsApprovedForAllMostrar todoCopiar
Hay tres formas en las que se puede permitir que una dirección transfiera un token:
- La dirección es el dueño del token.
- La dirección está aprobada para gastar ese token.
- La dirección es un operador para el propietario del token.
La función anterior puede ser una vista, porque no cambia el estado. Para reducir los costos operativos, cualquier función que pueda ser una vista debería ser una vista.
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] -= 1Mostrar todoCopiar
Cuando hay un problema con una transferencia se revierte la activación.
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_ADDRESSMostrar todoCopiar
Cambie el valor solo si es necesario. Las variables de estado viven en el storage. Escribir al almacenamiento es una de las operaciones más caras que hace la EVM (máquina virtual de Ethereum) (en términos de gas). Por lo tanto, es una buena idea minimizarlo, incluso si escribir el valor existente tiene un alto coste.
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 """Mostrar todoCopiar
Tenemos esta función interna, porque hay dos maneras de transferir tókenes (regulares y seguros), pero solo queremos una única ubicación en el código donde lo hacemos para simplificar la auditoría.
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)Mostrar todoCopiar
Para emitir un evento en Vyper, utiliza una instrucción de registro log
(ver aquí para más detalles(opens in a new tab)).
Funciones de transferencia
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)Mostrar todoCopiar
Esta función le permite transferir a una dirección arbitraria. A menos que la dirección sea un usuario, o un contrato que sepa cómo transferir tókenes, cualquier token que usted transfiera se quedará atascado en esa dirección e inutilizable.
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)Mostrar todoCopiar
Es mejor hacer la transferencia primero, porque si hay un problema lo revertiremos de todos modos, para que todas las operaciones de la activación se cancelen.
1 if _to.is_contract: # check if `_to` is a contract addressCopiar
Primero compruebe si la dirección es un contrato (si tiene código). De lo contrario, asuma que es una dirección de usuario y que el usuario podrá usar el token o transferirlo. No obstante, no se confíe con una falsa sensación de seguridad. Puede perder tókenes, incluso con safeTransferFrom
, si los transfiere a una dirección cuya clave privada nadie conozca.
1 returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)Copiar
Invoque al contrato de destino para ver si puede recibir tókenes 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
Si el destino es un contrato, pero no acepta tókenes ERC-721 (o que decidió no aceptar esta transferencia en particular), reviertalo.
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 != ownerMostrar todoCopiar
Por lo general, si usted no quiere un aprobador, nombre la dirección cero, no a usted mismo.
1 # Check requirements2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender3 senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]4 assert (senderIsOwner or senderIsApprovedForAll)Copiar
Para establecer una aprobación usted puede ser el propietario, o un operador autorizado por el propietario.
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)Mostrar todoCopiar
Acuñar nuevos tokens y destruir las existentes
La cuenta que creó el contrato es el minter
, el súper usuario autorizado a acuñar nuevos NFT. Sin embargo, ni siquiera se le permite quemar los tókenes existentes. Solo el propietario, o una entidad autorizada por el propietario, puede hacerlo.
1### MINT & BURN FUNCTIONS ###23@external4def mint(_to: address, _tokenId: uint256) -> bool:Copiar
Esta función siempre muestra True
, porque si la operación falla se revierte.
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.minterMostrar todoCopiar
Sólo el minter (la cuenta que creó el contrato ERC-721) puede acuñar nuevos tókenes. Esto puede ser un problema en el futuro si queremos cambiar la identidad del minter. En un contrato de producción, es deseable una función que permita al minter transferir privilegios de minter a otra persona.
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 lo general, el acuñar nuevos tókenes cuenta como una transferencia desde la dirección cero.
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)Mostrar todoCopiar
Cualquiera a quien se le permita transferir un token puede quemarlo. Mientras que una quema parece equivalente a transferira la dirección cero, la dirección cero no recibe el token. Esto nos permite liberar todo el almacenamiento que se utilizó para el token, lo que puede reducir el coste del gas de la transacción.
Utilizar este contrato
En contraste con Solidity, Vyper no tiene herencia. Esta es una elección de diseño deliberada para hacer el código más claro y por lo tanto más fácil de asegurar. Así que para crear su propio contrato Vyper ERC-721, utilice este contrato(opens in a new tab) y modifíquelo para implementar la lógica de negocio que desee.
Conclusión
A modo de recapitulación, he resumido algunas de las ideas más importantes de este contrato:
- Para recibir tokens ERC-721 con una transferencia segura, los contratos tienen que implementar la interfaz
ERC721Receiver
. - Incluso si utiliza una transferencia segura, los tókenes todavía pueden atascarse si los envía a una dirección cuya clave privada no se conozca.
- Cuando hay un problema con una operación, es una buena idea usar
revert
en la activación, en lugar de que solo aparezca un valor de fallo. - Las tókenes ERC-721 existen cuando tienen un propietario.
- Existen tres maneras de ser autorizados para transferir un NFT. Puede ser el propietario, ser aprobado para un token específico, o ser un operador para todos los tókenes del propietario.
- Los eventos pasados solo son visibles fuera de la cadena de bloques. El código ejecutándose dentro de la cadena de bloques no puede verlos.
Ahora vaya a implementar contratos seguros de Vyper.
Última edición: @nhsz(opens in a new tab), 15 de agosto de 2023