Guisa sul Contratto ERC-721 Vyper
Introduzione
Lo standard ERC-721 è utilizzato per determinare la proprietà di un Token Non Fungibile (NFT). I token ERC-20 si comportano come una commodity, perché non c'è differenza tra i token individuali. Al contrario, i token ERC-721 sono progettati per risorse simili ma non identiche, come diversi cat cartoon(opens in a new tab) o titoli di diverse proprietà immobiliari.
In questo articolo analizzeremo il contratto ERC-721 di Ryuya Nakamura(opens in a new tab). Questo contratto è scritto in Vyper(opens in a new tab), un linguaggio per contratti simile a Python, pensato per rendere più difficile scrivere codice non sicuro rispetto a Solidity.
Il contratto
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.vyCopia
I commenti in Vyper, come in Python, iniziano con un hash (#
) e continuano fino alla fine della riga. I commenti che includono @<keyword>
sono usati da NatSpec(opens in a new tab) per produrre una documentazione leggibile.
1from vyper.interfaces import ERC72123implements: ERC721Copia
L'interfaccia ERC-721 è creata nel linguaggio Vyper. Puoi vedere qui la definizione del codice(opens in a new tab). La definizione dell'interfaccia è scritta in Python, anziché in Vyper, perché le interfacce non sono usate solo nella blockchain, ma anche quando si invia una transazione alla blockchain da un client esterno, che potrebbe esser scritto in Python.
La prima riga importa l'interfaccia, la seconda specifica che la stiamo implementando qui.
L'interfaccia ERC721Receiver
1# Interface for the contract called by safeTransferFrom()2interface ERC721Receiver:3 def onERC721Received(Copia
ERC-721 supporta due tipi di trasferimento:
transferFrom
, che consente al mittente di specificare qualsiasi indirizzo di destinazione e pone sul mittente la responsabilità del trasferimento. Ciò significa che puoi trasferire a un indirizzo non valido, nel qual caso l'NFT è perso definitivamente.safeTransferFrom
, che controlla se l'indirizzo di destinazione è un contratto. In tal caso, il contratto ERC-721 chiede al contratto ricevente se vuole ricevere l’NFT.
Per rispondere alle richieste safeTransferFrom
, un contratto ricevente deve implementare ERC721Receiver
.
1 _operator: address,2 _from: address,Copia
L'indirizzo _from
è il proprietario corrente del token. L'indirizzo _operator
è quello che ha richiesto il trasferimento (i due potrebbero non corrispondere, a causa delle indennità).
1 _tokenId: uint256,Copia
Gli ID del token ERC-721 sono a 256 bit. Solitamente sono creati mediante hashing di una descrizione di qualsiasi token rappresenti.
1 _data: Bytes[1024]Copia
La richiesta può avere fino a 1024 byte di dati utente.
1 ) -> bytes32: viewCopia
Per impedire casi la possibilità che un contratto accetti accidentalmente un trasferimento, il valore restituito non è booleano, ma 256 bit con un valore specifico.
Questa funzione è una view
, ovvero può leggere lo stato della blockchain, ma non modificarlo.
Eventi
Gli eventi(opens in a new tab) sono emessi per informare gli utenti e i server al di fuori della blockchain degli eventi. Nota che il contenuto degli eventi non è disponibile per i contratti sulla 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)Mostra tuttoCopia
Questo è simile all'evento di Trasferimento dell'ERC-20, tranne per il fatto che segnaliamo un tokenId
anziché un importo. Nessuno possiede l'indirizzo zero, quindi per convenzione lo usiamo per segnalare la creazione e distruzione dei token.
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)Mostra tuttoCopia
L'approvazione di un ERC-721 è simile a un'indennità dell'ERC-20. Un indirizzo specifico può trasferire un token specifico. Questo offre ai contratti un meccanismo per rispondere quando accettano un token. I contratti non possono ascoltare gli eventi, quindi se semplicemente trasferisci loro il token, non lo "sanno". In questo modo, il proprietario invia prima un'approvazione e poi una richiesta al contratto: "Ho approvato il tuo trasferimento del token X, sei pregato di...".
Si tratta di una scelta di progettazione per rendere lo standard ERC-721 simile allo standard ERC-20. Poiché i token di ERC-721 non sono fungibili, un contratto può capire di aver ricevuto un token specifico anche guardando alle sue proprietà.
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: boolMostra tuttoCopia
Talvolta, è utile avere un operatore, che possa gestire tutti i token di un conto di un tipo specifico (quelli gestiti da un contratto specifico), similmente a una delega. Ad esempio, potrei voler dare a un contratto una delega per verificare se non l'ho contattato per sei mesi e, in questo caso, distribuisce le mie risorse ai miei eredi (se uno di loro lo richiede, i contratti non possono fare niente senza esser chiamati da una transazione). In ERC-20 possiamo solo dare un'indennità elevata a un contratto di ereditarietà, ma questo non funziona per ERC-721 perché i token non sono fungibili. Questo è l'equivalente.
Il valore approved
ci comunica se l'evento è per un'approvazione, o la revoca di un'approvazione.
Variabili di stato
Queste variabili contengono lo stato corrente dei token: quali sono disponibili e chi li possiede. Gran parte di questi sono oggetti di HashMap
, mappature unidirezionali che esistono tra due tipi(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]Copia
Le identità dell'utente e del contratto su Ethereum sono rappresentate da indirizzi a 160 bit. Queste due variabili mappano gli ID dei token con i loro proprietari e quelli approvati per trasferirli (a un massimo di uno ciascuno). In Ethereum, i dati non inizializzati sono sempre zero, quindi se non c'è alcun proprietario o trasferente approvato, il valore per quel token è zero.
1# @dev Mapping from owner address to count of his tokens.2ownerToNFTokenCount: HashMap[address, uint256]Copia
Questa variabile tiene conto dei token per ogni proprietario. Non c'è alcuna mappatura dai proprietari ai token, quindi l'unico modo per identificare i token che un proprietario specifico possiede è guardare alla cronologia di eventi della blockchain e vedere gli eventi di trasferimento
appropriati. Possiamo usare questa variabile per sapere quando abbiamo tutti gli NFT e non dobbiamo guardare oltre nel tempo.
Questo algoritmo funziona solo per le interfacce utente e i server esterni. Il codice in esecuzione sulla blockchain stessa non può leggere gli eventi passati.
1# @dev Mapping from owner address to mapping of operator addresses.2ownerToOperators: HashMap[address, HashMap[address, bool]]Copia
Un conto potrebbe avere più di un singolo operatore. Un semplice HashMap
è insufficiente per tenerne traccia, perché ogni chiave conduce a un valore singolo. Invece, puoi usare HashMap[address, bool]
come valore. Di default, il valore per ogni indirizzo è False
, che significa che non è un operatore. Puoi impostare i valori a True
se necessario.
1# @dev Address of minter, who can mint a token2minter: addressCopia
I nuovi token devono in qualche modo esser creati. In questo contratto, esiste solo un'entità che può farlo, il coniatore
. Questo sarà probabilmente sufficiente per un gioco, ad esempio. Per altri scopi, potrebbe esser necessario creare una logica di business più complicata.
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) = 0x0000000000000000000000000000000000000000000000000000000080ac58cdCopia
ERC-165(opens in a new tab) specifica un meccanismo con cui un contratto può rivelare come le applicazioni possono comunicare con esso, a quali ERC è conforme. In questo caso, il contratto è conforme a ERC-165 ed ERC-721.
Funzioni
Queste sono le funzioni che implementano effettivamente ERC-721.
Costruttore
1@external2def __init__():Copia
In Vyper, come in Python, la funzione del costruttore è chiamata __init__
.
1 """2 @dev Contract constructor.3 """Copia
Su Python e su Vyper, puoi anche creare un commento specificando una stringa su più righe (che inizia e termina per """
), senza usarla in alcun modo. Questi commenti possono anche includere NatSpec(opens in a new tab).
1 self.supportedInterfaces[ERC165_INTERFACE_ID] = True2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True3 self.minter = msg.senderCopia
Per accedere alle variabili di stato, si usa self.<variable name>
(di nuovo, come in Python).
Funzioni di visualizzazione
Sono funzioni che non modificano lo stato della blockchain e dunque sono eseguibili liberamente se chiamate esternamente. Se le funzioni di visualizzazione sono chiamate da un contratto, devono comunque esser eseguite su ogni nodo e, dunque, costano gas.
1@view2@externalCopia
Queste parole chiave prima della definizione di una funzione che inizia con un segno (@
) sono dette decorazioni. Specificano le circostanze in cui una funzione è chiamabile.
@view
specifica che questa funzione è una visualizzazione.@external
specifica che questa particolare funzione è chiamabile dalle transazioni o da altri contratti.
1def supportsInterface(_interfaceID: bytes32) -> bool:Copia
A differenza di Python, Vyper è un linguaggio tipizzato statico(opens in a new tab). Non puoi dichiarare una variabile, o il parametro di una funzione, senza indicare il tipo di dato(opens in a new tab). In questo caso, il parametro inserito è bytes32
, un valore a 256 bit (256 bit è la dimensione nativa della word della Macchina Virtuale di Ethereum). L'output è un valore booleano. Per convenzione, i nomi dei parametri della funzione iniziano con un trattino basso (_
).
1 """2 @dev Interface identification is specified in ERC-165.3 @param _interfaceID Id of the interface4 """5 return self.supportedInterfaces[_interfaceID]Copia
Restituisce il valore dall'HashMap self-supportedInterfaces
, che è impostata nel costruttore (__init__
).
1### VIEW FUNCTIONS ###Copia
Queste sono le funzioni di visualizzazione che rendono le informazioni sui token disponibili a utenti e altri contratti.
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_ADDRESSMostra tuttoCopia
Questa riga afferma(opens in a new tab) che _owner
non è zero. Se lo è, c'è un errore e l'operazione è annullata.
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 ownerMostra tuttoCopia
Nella Macchina Virtuale di Ethereum (EVM), ogni memoria senza un valore memorizzato è zero. Se non esiste alcun token a _tokenId
, allora il valore di self.idToOwner[_tokenId]
è zero. In quel caso la funzione si annulla.
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]Mostra tuttoCopia
Nota che getApproved
può restituire zero. Se il token è valido, restituisce self.idToApprovals[_tokenId]
. Se non c'è alcun approvatore, quel valore è 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]Mostra tuttoCopia
Questa funzione verifica se _operator
può gestire tutti i token del _owner
in questo contratto. Poiché possono esserci diversi operatori, si tratta di un HashMap a due livelli.
Funzioni d'aiuto al trasferimento
Queste funzioni implementano operazioni che fanno parte del trasferimento o della gestione dei token.
12### TRANSFER FUNCTION HELPERS ###34@view5@internalCopia
Questa decorazione, @internal
, significa che la funzione è accessibile solo da altre funzioni nello stesso contratto. Per convenzione, questi nomi di funzione iniziano anch'essi con un trattino basso (_
).
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 spenderIsApprovedForAllMostra tuttoCopia
Esistono tre modi in cui a un indirizzo è consentito trasferire un token:
- L'indirizzo è il proprietario del token
- L'indirizzo è autorizzato a spendere quel token
- L'indirizzo è un operatore per il proprietario del token
La funzione che precedere può essere una visualizzazione, perché non modifica lo stato. Per ridurre i costi operativi, ogni funzione che può essere una visualizzazione, dovrebbe esserlo.
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] -= 1Mostra tuttoCopia
Quando c'è un problema con un trasferimento, anulliamo la chiamata.
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_ADDRESSMostra tuttoCopia
Cambia il valore solo se necessario. Le variabili di stato risiedono nella memoria. La scrittura all'archiviazione è una delle operazioni più costose che l'EVM (Macchina Virtuale di Ethereum) effettua (in termini di gas). Dunque, è bene mantenerla al minimo, anche scrivere il valore esistente ha un costo elevato.
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 """Mostra tuttoCopia
Abbiamo questa funzione interna perché esistono due modi per trasferire i token (regolare e sicuro), ma vogliamo una sola posizione nel codice dove farlo, per semplificare il controllo.
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)Mostra tuttoCopia
Per emettere un evento su Vyper, si usa una dichiarazione di log
(vedi qui per ulteriori dettagli(opens in a new tab)).
Funzioni di trasferimento
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)Mostra tuttoCopia
Questa funzione ti consente di trasferire a un indirizzo arbitrario. A meno che l'indirizzo non sia un utente o un contratto che sa come trasferire i token, ogni token che trasferisci sarà bloccato in quell'indirizzo e inutile.
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)Mostra tuttoCopia
Va bene effettuare prima il trasferimento perché se c'è un problema, ripristineremo comunque, quindi tutto ciò che è fatto nella chiamata sarà annullato.
1 if _to.is_contract: # check if `_to` is a contract addressCopia
Prima controlla per vedere se l'indirizzo è un contratto (se ha il codice). Altrimenti, presumi che sia un indirizzo utente e che l'utente possa usare o trasferire il token. Ma non abbandonarti a un falso senso di sicurezza. Puoi infatti perdere i token, anche con safeTransferFrom
, se li trasferisci a un indirizzo di cui nessuno conosce la chiave privata.
1 returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)Copia
Chiama il contratto di destinazione per vedere se può ricevere i token 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)Copia
Se la destinazione è un contratto, ma un contratto che non accetta i token ERC-721 (o che ha deciso di non accettare questo specifico trasferimento), annulla.
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 != ownerMostra tuttoCopia
Per convenzione, se non vuoi avere un approvatore, nomini l'indirizzo zero, non te stesso.
1 # Check requirements2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender3 senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]4 assert (senderIsOwner or senderIsApprovedForAll)Copia
Per impostare un'approvazione, puoi essere il proprietario o un operatore autorizzato dal proprietario.
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)Mostra tuttoCopia
Conia nuovi token e distruggi token esistenti
Il conto che ha creato il contratto è il minter
, il super utente autorizzato a coniare nuovi NFT. Tuttavia, nemmeno lui è autorizzato a bruciare i token esistenti. Può farlo solo il proprietario, o un'entità da autorizzata dal proprietario.
1### MINT & BURN FUNCTIONS ###23@external4def mint(_to: address, _tokenId: uint256) -> bool:Copia
Questa funzione restituisce sempre True
, perché se l'operazione fallisce è ripristinata.
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.minterMostra tuttoCopia
Solo il coniatore (il conto che ha creato il contratto ERC-721) può coniare nuovi token. Questo può essere un problema in futuro se si vuole cambiare l'identità del coniatore. In un contratto di produzione, potresti volere una funzione che consenta al coniatore di trasferire i propri privilegi a qualcun altro.
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 TrueCopia
Per convenzione, coniare i nuovi token conta come un trasferimento all'indirizzo 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)Mostra tuttoCopia
Chiunque è autorizzato a trasferire un token, può bruciarlo. Anche se bruciare un token appare equivalente a trasferirlo all'indirizzo zero, l'indirizzo zero non riceve realmente il token. Ciò ci consente di liberare tutta l'archiviazione usata per il token, riducendo il costo del gas della transazione.
Usare questo contratto
A differenza di Solidity, Vyper non ha un ereditarietà. Si tratta di una scelta progettuale deliberata per rendere il codice più chiaro e quindi più facile da proteggere. Quindi, per creare il tuo contratto ERC-721 in Vyper, prendi questo contratto(opens in a new tab) e lo modifichi per implementare la logica di business che desideri.
Conclusione
Per ripasso presentiamo alcune delle idee più importanti in questo contratto:
- Per ricevere i token ERC-721 con un trasferimento sicuro, i contratti devono implementare l'interfaccia di
ERC721Receiver
. - Anche se usi il trasferimento sicuro, i token possono comunque rimanere bloccati se li invii a un indirizzo la cui chiave privata è sconosciuta.
- Quando c'è un problema con un'operazione, è una buona idea eseguire il
revert
della chiamata, piuttosto che restituire semplicemente un valore d'errore. - I token ERC-721 esistono quando hanno un proprietario.
- Esistono tre modi per essere autorizzati a trasferire un NFT. Puoi essere il proprietario, essere approvato per un token specifico o essere un operatore per tutti i token del proprietario.
- Gli eventi passati sono visibili solo al di fuori della blockchain. Il codice eseguito nella blockchain non può vederli.
Ora puoi andare a implementare contratti sicuri in Vyper.
Ultima modifica: @nhsz(opens in a new tab), 15 agosto 2023