Vai al contenuto principale

Aiuta ad aggiornare questa pagina

🌏

C'è una nuova versione di questa pagina, ma al momento è solo in inglese. Aiutaci a tradurre l'ultima versione.

Traduci la pagina
Visualizza in inglese

Nessun bug qui!🐛

Questa pagina non è stata tradotta. Per il momento, è stata intenzionalmente lasciata in inglese.

Guisa sul Contratto ERC-721 Vyper

vyper
erc-721
python
Principiante
✍Ori Pomerantz
📆1 aprile 2021
⏱️20 minuti letti

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 o titoli di diverse proprietà immobiliari.

In questo articolo analizzeremo il contratto ERC-721 di Ryuya Nakamura. Questo contratto è scritto in Vyper, 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.vy
4
📋 Copia

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 per produrre una documentazione leggibile.

1from vyper.interfaces import ERC721
2
3implements: ERC721
4
📋 Copia

L'interfaccia ERC-721 è creata nel linguaggio Vyper. Puoi vedere qui la definizione del codice. 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(
4
📋 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,
3
📋 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,
2
📋 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]
2
📋 Copia

La richiesta può avere fino a 1024 byte di dati utente.

1 ) -> bytes32: view
2
📋 Copia

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 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 are
2# created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any
3# number of NFTs may be created and assigned without emitting Transfer. At the time of any
4# 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 transfered.
8event Transfer:
9 sender: indexed(address)
10 receiver: indexed(address)
11 tokenId: indexed(uint256)
12
Mostra tutto
📋 Copia

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 zero
2# address indicates there is no approved address. When a Transfer event emits, this also
3# 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)
11
Mostra tutto
📋 Copia

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 manage
2# 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 if
6# revoked).
7event ApprovalForAll:
8 owner: indexed(address)
9 operator: indexed(address)
10 approved: bool
11
Mostra tutto
📋 Copia

Talvolta è utile avere un operatore che possa gestire tutti i token di un account 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.

1# @dev Mapping from NFT ID to the address that owns it.
2idToOwner: HashMap[uint256, address]
3
4# @dev Mapping from NFT ID to approved address.
5idToApprovals: HashMap[uint256, address]
6
📋 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]
3
📋 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]]
3
📋 Copia

Un account 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 token
2minter: address
3
📋 Copia

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 supported
2supportedInterfaces: HashMap[bytes32, bool]
3
4# @dev ERC165 interface ID of ERC165
5ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
6
7# @dev ERC165 interface ID of ERC721
8ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd
9
📋 Copia

ERC-165 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@external
2def __init__():
3
📋 Copia

In Vyper, come in Python, la funzione del costruttore è chiamata __init__.

1 """
2 @dev Contract constructor.
3 """
4
📋 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.

1 self.supportedInterfaces[ERC165_INTERFACE_ID] = True
2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True
3 self.minter = msg.sender
4
📋 Copia

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 essere eseguite su ogni nodo e dunque costano del carburante.

1@view
2@external
3
📋 Copia

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:
2
📋 Copia

A differenza di Python, Vyper è un linguaggio tipizzato statico. Non puoi dichiarare una variabile, o il parametro di una funzione, senza indicare il tipo di dato. 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 interface
4 """
5 return self.supportedInterfaces[_interfaceID]
6
📋 Copia

Restituisce il valore dall'HashMap self-supportedInterfaces, che è impostata nel costruttore (__init__).

1### VIEW FUNCTIONS ###
2
📋 Copia

Queste sono le funzioni di visualizzazione che rendono le informazioni sui token disponibili a utenti e altri contratti.

1@view
2@external
3def 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_ADDRESS
10
Mostra tutto
📋 Copia

Questa riga afferma che _owner non è zero. Se lo è, c'è un errore e l'operazione è annullata.

1 return self.ownerToNFTokenCount[_owner]
2
3@view
4@external
5def 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 NFT
13 assert owner != ZERO_ADDRESS
14 return owner
15
Mostra tutto
📋 Copia

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@view
2@external
3def 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 NFT
10 assert self.idToOwner[_tokenId] != ZERO_ADDRESS
11 return self.idToApprovals[_tokenId]
12
Mostra tutto
📋 Copia

Nota che getApproved può restituire zero. Se il token è valido, restituisce self.idToApprovals[_tokenId]. Se non c'è alcun approvatore, quel valore è zero.

1@view
2@external
3def 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]
10
Mostra tutto
📋 Copia

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.

1
2### TRANSFER FUNCTION HELPERS ###
3
4@view
5@internal
6
📋 Copia

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 ID
4 @param spender address of the spender to query
5 @param tokenId uint256 ID of the token to be transferred
6 @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 token
8 """
9 owner: address = self.idToOwner[_tokenId]
10 spenderIsOwner: bool = owner == _spender
11 spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId]
12 spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender]
13 return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll
14
Mostra tutto
📋 Copia

Esistono tre modi in cui a un indirizzo è consentito trasferire un token:

  1. L'indirizzo è il proprietario del token
  2. L'indirizzo è autorizzato a spendere quel token
  3. 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@internal
2def _addTokenTo(_to: address, _tokenId: uint256):
3 """
4 @dev Add a NFT to a given address
5 Throws if `_tokenId` is owned by someone.
6 """
7 # Throws if `_tokenId` is owned by someone
8 assert self.idToOwner[_tokenId] == ZERO_ADDRESS
9 # Change the owner
10 self.idToOwner[_tokenId] = _to
11 # Change count tracking
12 self.ownerToNFTokenCount[_to] += 1
13
14
15@internal
16def _removeTokenFrom(_from: address, _tokenId: uint256):
17 """
18 @dev Remove a NFT from a given address
19 Throws if `_from` is not the current owner.
20 """
21 # Throws if `_from` is not the current owner
22 assert self.idToOwner[_tokenId] == _from
23 # Change the owner
24 self.idToOwner[_tokenId] = ZERO_ADDRESS
25 # Change count tracking
26 self.ownerToNFTokenCount[_from] -= 1
27
Mostra tutto
📋 Copia

Quando c'è un problema con un trasferimento, anulliamo la chiamata.

1@internal
2def _clearApproval(_owner: address, _tokenId: uint256):
3 """
4 @dev Clear an approval of a given address
5 Throws if `_owner` is not the current owner.
6 """
7 # Throws if `_owner` is not the current owner
8 assert self.idToOwner[_tokenId] == _owner
9 if self.idToApprovals[_tokenId] != ZERO_ADDRESS:
10 # Reset approvals
11 self.idToApprovals[_tokenId] = ZERO_ADDRESS
12
Mostra tutto
📋 Copia

Cambia il valore solo se necessario. Le variabili di stato risiedono nella memoria. Scrivere alla memoria è una delle operazioni più costose che l'EVM (Macchina Virtuale di Ethereum) effettua (in termini di carburante). Dunque, è bene mantenerla al minimo, anche scrivere il valore esistente ha un costo elevato.

1@internal
2def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address):
3 """
4 @dev Exeute transfer of a NFT.
5 Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
6 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 """
11
Mostra tutto
📋 Copia

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 requirements
2 assert self._isApprovedOrOwner(_sender, _tokenId)
3 # Throws if `_to` is the zero address
4 assert _to != ZERO_ADDRESS
5 # Clear approval. Throws if `_from` is not the current owner
6 self._clearApproval(_from, _tokenId)
7 # Remove NFT. Throws if `_tokenId` is not a valid NFT
8 self._removeTokenFrom(_from, _tokenId)
9 # Add NFT
10 self._addTokenTo(_to, _tokenId)
11 # Log the transfer
12 log Transfer(_from, _to, _tokenId)
13
Mostra tutto
📋 Copia

Per emettere un evento su Vyper, si usa una dichiarazione di log (vedi qui per ulteriori dettagli).

Funzioni di trasferimento

1
2### TRANSFER FUNCTIONS ###
3
4@external
5def transferFrom(_from: address, _to: address, _tokenId: uint256):
6 """
7 @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved
8 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 else
13 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)
19
Mostra tutto
📋 Copia

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@external
2def 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 the
11 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 if
16 the return value is not `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
17 NOTE: bytes4 is represented by bytes32 with padding
18 @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)
24
Mostra tutto
📋 Copia

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 address
2
📋 Copia

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)
2
📋 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)
3
📋 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@external
2def 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 NFT
13 assert owner != ZERO_ADDRESS
14 # Throws if `_approved` is the current owner
15 assert _approved != owner
16
Mostra tutto
📋 Copia

Per convenzione, se non vuoi avere un approvatore, nomini l'indirizzo zero, non te stesso.

1 # Check requirements
2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender
3 senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]
4 assert (senderIsOwner or senderIsApprovedForAll)
5
📋 Copia

Per impostare un'approvazione, puoi essere il proprietario o un operatore autorizzato dal proprietario.

1 # Set the approval
2 self.idToApprovals[_tokenId] = _approved
3 log Approval(owner, _approved, _tokenId)
4
5
6@external
7def setApprovalForAll(_operator: address, _approved: bool):
8 """
9 @dev Enables or disables approval for a third party ("operator") to manage all of
10 `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.sender
18 self.ownerToOperators[msg.sender][_operator] = _approved
19 log ApprovalForAll(msg.sender, _operator, _approved)
20
Mostra tutto
📋 Copia

Conia nuovi token e distruggi token esistenti

L'account che ha creato il contratto è il coniatore, 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 ###
2
3@external
4def mint(_to: address, _tokenId: uint256) -> bool:
5
📋 Copia

Questa funzione restituisce sempre True, perché se l'operazione fallisce è ripristinata.

1 """
2 @dev Function to mint tokens
3 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 minter
11 assert msg.sender == self.minter
12
Mostra tutto
📋 Copia

Solo il coniatore (l'account 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 address
2 assert _to != ZERO_ADDRESS
3 # Add NFT. Throws if `_tokenId` is owned by someone
4 self._addTokenTo(_to, _tokenId)
5 log Transfer(ZERO_ADDRESS, _to, _tokenId)
6 return True
7
📋 Copia

Per convenzione, coniare i nuovi token conta come un trasferimento all'indirizzo zero.

1
2@external
3def 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 approved
7 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 requirements
12 assert self._isApprovedOrOwner(msg.sender, _tokenId)
13 owner: address = self.idToOwner[_tokenId]
14 # Throws if `_tokenId` is not a valid NFT
15 assert owner != ZERO_ADDRESS
16 self._clearApproval(owner, _tokenId)
17 self._removeTokenFrom(owner, _tokenId)
18 log Transfer(owner, ZERO_ADDRESS, _tokenId)
19
Mostra tutto
📋 Copia

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. Questo ci consente di liberare tutta la memoria usata per il token, potendo così ridurre il costo del carburante 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 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: , Invalid DateTime
Modifica la pagina

Questa pagina è stata utile?