Panduan Lengkap Kontrak ERC-721 Vyper
Pendahuluan
Standar ERC-721 digunakan untuk memegang kepemilikan Token yang Tak Dapat Dipertukarkan (NFT). Token ERC-20 berperilaku sebagai komoditas, karena tidak ada perbedaan antara token individu. Sebaliknya, token ERC-721 dirancang untuk aset yang serupa tetapi tidak sama, seperti kartun kucing(opens in a new tab) atau judul untuk bagian dari real estate yang berbeda.
Dalam artikel ini kita akan menganalisa kontrak ERC-721 Ryuya Nakamura(opens in a new tab). Kontrak ini ditulis dalam Vyper(opens in a new tab), bahasa kontrak seperti Python yang dirancang untuk lebih menyulitkan penulisan kode yang tidak aman ketimbang yang ada di Solidity.
Kontrak
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.vySalin
Komentar pada Vyper, seperti juga pada Python, diawali dengan tanda pagar (#
) dan dilanjutkan hingga akhir baris. Komentar yang menggunakan @<keyword>
digunakan oleh NatSpec(opens in a new tab) untuk memproduksi dokumentasi yang dapat dibaca oleh manusia.
1from vyper.interfaces import ERC72123implements: ERC721Salin
Antarmuka ERC-721 dibangun dalam bahasa Vyper. Anda dapat melihat definisi kode tersebut di sini(opens in a new tab). Definisi antarmuka ditulis dalam Python, alih-alih Vyper, karena antarmuka tidak hanya digunakan dalam blockchain, tetapi juga saat mengirimkan transaksi ke blockchain dari klien eksternal, yang mungkin saja ditulis dalam Python.
Baris pertama mengimpor antarmuka, dan baris kedua menunjukkan bahwa kita mengimplementasikannya di sini.
Antarmuka ERC721Receiver
1# Interface for the contract called by safeTransferFrom()2interface ERC721Receiver:3 def onERC721Received(Salin
ERC-721 mendukung dua jenis transfer:
transferFrom
, yang memungkinkan pengirim menentukan alamat tujuan dan meletakkan tanggung jawab pentransferan pada pengirimnya. Ini berarti Anda dapat mentransfer ke alamat tidak valid, yang berarti pula NFT akan hilang jika dikirim ke alamat tersebut.safeTransferFrom
, yang memeriksa apakah alamat tujuannya merupakan sebuah kontrak atau bukan. Jika memang demikian, maka kontrak ERC-721 akan menanyakan kontrak penerima apakah ia ingin menerima NFT atau tidak.
Untuk menjawab permintaan safeTransferFrom
kontrak penerima harus mengimplementasikan ERC721Receiver
.
1 _operator: address,2 _from: address,Salin
Alamat _from
adalah pemiliki token saat ini. Alamat _operator
adalah yang meminta transfer (keduanya mungkin tidak sama, karena perbedaan uang tunjangan).
1 _tokenId: uint256,Salin
ID token ERC-721 adalah 256 bit. Biasanya ID token itu diciptakan dengan melakukan hash terhadap deskripsi mengenai apapun yang direpresentasikan token tersebut.
1 _data: Bytes[1024]Salin
Permintaan dapat memiliki hingga 1024 bita data pengguna.
1 ) -> bytes32: viewSalin
Untuk menghindari kejadian di mana kontrak secara tidak sengaja menerima transfer, nilai pengembaliannya bukan merupakan boolean, melainkan 256 bit dengan nilai spesifik.
Fungsi ini merupakan sebuah view
, yang berarti dapat membaca status blockchain tersebut, tetapi tidak dapat memodifikasinya.
Aksi
Aksi(opens in a new tab) dipancarkan untuk memberitahu aksi kepada pengguna dan server yang ada di luar blockchain. Perhatikan bahwa konten aksi tidak tersedia untuk kontrak di 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 transfered.8event Transfer:9 sender: indexed(address)10 receiver: indexed(address)11 tokenId: indexed(uint256)Tampilkan semuaSalin
Ini sama dengan aksi Transfer ERC-20, kecuali bahwa kita melaporkan tokenId
ketimbang suatu jumlah. Tidak ada seorangpun yang memiliki alamat nol, sehingga secara konvensi, kita menggunakannya untuk melaporkan pembuatan dan penghancuran 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)Tampilkan semuaSalin
Persetujuan ERC-721 sama dengan uang tunjangan ERC-20. Suatu alamat tertentu diizinkan untuk mentransfer token tertentu. Ini memberikan mekanisme bagi kontrak untuk merespons saat mereka menerima token. Kontrak tidak dapat mendengarkan kejadian, sehingga jika Anda hanya mentransfer token ke mereka, mereka tidak "tahu" tentang itu. Dalam cara ini, pemilik pertama mengirim persetujuan dan kemudian mengirim permintaan ke kontrak: "Saya menyetujui Anda mentransfer token X, silahkan lakukan ...".
Ini adalah pilihan rancangan untuk membuat standar ERC-721 serupa dengan standar ERC-20. Karena token ERC-721 tidak dapat dipertukarkan, suatu kontrak juga dapat mengenali bahwa ia mendapatkan token tertentu dengan melihat kepemilikan 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: boolTampilkan semuaSalin
Terkadang ada gunanya memiliki operator yang dapat mengelola semua token akun dari jenis tertentu (yang dikelola oleh kontrak tertentu), sama seperti surat kuasa. Contohnya, saya mungkin ingin memberi kuasa tersebut ke kontrak yang memeriksa apakah saya belum menghubunginya selama enam bulan, dan jika demikian bagikan aset saya kepada pewaris saya (jika salah satu dari mereka memintanya, kontrak tidak dapat melakukan apa pun tanpa dipanggil oleh transaksi). Dalam ERC-20, kita hanya dapat memberi uang tunjangan besar ke kontrak warisan, tetapi itu tidak bekerja di ERC-721 karena token tidak dapat dipertukarkan. Ini bersifat setara.
Nilai approved
memberi tahu kita apakah aksi merupakan persetujuan, atau penarikan dari persetujuan.
Variabel State
Variabel ini berisi state token saat ini: yang mana yang tersedia dan siapa yang memilikinya. Kebanyakan dari ini merupakan objek HashMap
, pemetaan satu arah yang ada di antara dua jenis(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]Salin
Indentitas pengguna dan kontrak di Ethereum diwakili oleh alamat 160 bit. Kedua variable tersebut dipetakan dari ID token pemilik mereka dan siapa pun yang setuju untuk mentransfernya (dengan jumlah maksimum satu untuk setiap pemilik). Dalam Ethereum, data yang tidak terinisialisasi selalu bernilai nol, jadi jika tidak ada pemilik atau pentransfer yang menyetujui, nilai token tersebut menjadi nol.
1# @dev Mapping from owner address to count of his tokens.2ownerToNFTokenCount: HashMap[address, uint256]Salin
Variabel ini menampung hitungan token untuk setiap pemilik. Tidak ada pemetaan dari pemilik ke token, sehingga satu-satunya cara untuk mengidentifikasi token yang dimiliki pemilik tertentu adalah dengan melihat kembali di riwayat aksi blockchain-nya dan melihat aksi Transfer
yang sesuai. Kita dapat menggunakan variabel ini untuk mengetahui kapan kita mendapat semua NFT tersebut dan tidak perlu memeriksanya beberapa kali sepanjang waktu.
Ingatlah bahwa algoritma ini hanya bekerja untuk antarmuka pengguna dan server eksternal. Kode yang beroperasi pada blockchain itu sendiri tidak dapat membaca aksi lampau.
1# @dev Mapping from owner address to mapping of operator addresses.2ownerToOperators: HashMap[address, HashMap[address, bool]]Salin
Sebuah akun mungkin saja dapat memiliki lebih dari satu operator. Sebuah HashMap
sederhana tidak cukup untuk terus melacaknya, karena setiap kunci mengarah ke sebuah nilai tunggal. Alih-alih, Anda dapat menggunakan HashMap[address, bool]
sebagai nilai. Secara bawaan, nilai untuk setiap alamat adalah False
, yang berarti ini bukanlah sebuah operator. Anda dapat menetapkan nilai ke True
sesuai keperluan.
1# @dev Address of minter, who can mint a token2minter: addressSalin
Token baru telah dibuat. Dalam kontrak ini ada entitas tunggal yang diizinkan untuk melakukannya, minter
. Sebagai contoh, ini mungkin cukup untuk sebuah permainan. Untuk keperluan lainnya, membuat logika bisnis yang lebih rumit mungkin diperlukan.
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) = 0x0000000000000000000000000000000000000000000000000000000080ac58cdSalin
ERC-165(opens in a new tab) menentukan mekanisme pada kontrak untuk mengungkapkan cara agar aplikasi dapat berkomunikasi dengannya, ke ERC mana yang akan ia sesuaikan. Dalam kasus ini, kontrak menyesuaikan dengan ERC-165 dan ERC-721.
Fungsi
Ini adalah fungsi-fungsi yang benar-benar mengimplementasikan ERC-721.
Konstruktor
1@external2def __init__():Salin
Pada Vyper, seperti di Python, fungsi konstruktor disebut __init__
.
1 """2 @dev Contract constructor.3 """Salin
Di Python, dan di Vyper, Anda juga dapat membuat komentar dengan menentukan string multibaris (yang dimulai dan diakhiri dengan """
), dan tidak menggunakannya sama sekali. Komentar ini juga dapat mencakup NatSpec(opens in a new tab).
1 self.supportedInterfaces[ERC165_INTERFACE_ID] = True2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True3 self.minter = msg.senderSalin
Untuk mengakses variable status, Anda menggunakan self.<variable name>
(sekali lagi, sama seperti di Python).
Lihat Fungsi
Berikut adalah fungsi-fungsi yang tidak mengubah state blockchain, dan karenanya dapat dieksekusikan secara bebas jika dipanggil secara eksternal. Jika fungsi tampilan dipanggil oleh sebuah kontrak, fungsi tersebut masih harus dieksekusi di setiap node dan karena itu terkena biaya gas.
1@view2@externalSalin
Kata kunci berikut yang berhubungan dengan definisi fungsi yang dimulai dengan tanda at (@
) disebut dekorasi. Tanda ini menentukan keadaan dimana sebuah fungsi dapat dipanggil.
@view
menunjukkan bahwa fungsi ini merupakan sebuah tampilan.@external
menunjukkan bahwa fungsi tersebut dapat dipanggil oleh transaksi dan oleh kontrak lainnya.
1def supportsInterface(_interfaceID: bytes32) -> bool:Salin
Berkebalikan dengan Python, Vyper adalah bahasa berjenis statis(opens in a new tab). Anda tidak dapat mendeklarasikan sebuah variabel, atau sebuah fungsi parameter, tanpa mengidentifikasi tipe datanya(opens in a new tab). Dalam kasus ini parameter inputnya adalah bytes32
, sebuah nilai 256-bit (256 bit adalah ukuran kata asal dari Mesin Virtual Ethereum). Keluarannya berupa nilai boolean. Secara konvensi, nama parameter fungsinya dimulai dengan garis bawah (_
).
1 """2 @dev Interface identification is specified in ERC-165.3 @param _interfaceID Id of the interface4 """5 return self.supportedInterfaces[_interfaceID]Salin
Kembalikan nilai dari HashMap self.supportedInterfaces
, yang ditetapkan dalam konstruktor (__init__
).
1### VIEW FUNCTIONS ###Salin
Berikut adalah fungsi-fungsi tampilan yang membuat informasi mengenai token tersedia untuk pengguna dan kontrak-kontrak lainnya.
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_ADDRESSTampilkan semuaSalin
Baris ini menegaskan(opens in a new tab) bahwa _owner
bukan nol. Jika benar demikian, maka ada kesalahan dan operasi akan dibalikkan.
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 ownerTampilkan semuaSalin
Dalam Mesin Virtual Ethereum (evm), penyimpanan mana pun yang tidak memiliki nilai yang tersimpan di dalamnya adalah nol. Jika tidak ada token pada _tokenId
maka nilai dari self.idToOwner[_tokenId]
adalah nol. Dalam kasus tersebut fungsinya melakukan pembalikan.
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]Tampilkan semuaSalin
Ingatlah bahwa getApproved
dapat mengembalikan nol. Jika tokennya valid, maka akan mengembalikan self.idToApprovals[_tokenId]
. Jika tidak ada pemberi persetujuan maka nilainya adalah nol.
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]Tampilkan semuaSalin
Fungsi ini memeriksa jika _operator
diizinkan mengelola semua token milik _owner
dalam kontrak ini. Dikarenakan dapat terjadi multioperator, ini merupakan HashMap dua tingkat.
Fungsi Transfer Pembantu
Fungsi-fungsi berikut mengimplementasikan operasi yang merupakan bagian pentransferan atau pengelolaan token.
12### TRANSFER FUNCTION HELPERS ###34@view5@internalSalin
Dekorasi ini, @internal
, berarti bahwa fungsi hanya dapat diakses dari fungsi lainnya dalam kontrak yang sama. Secara konvensi, nama fungsi ini juga dimulai dengan garis bawah (_
).
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 spenderIsApprovedForAllTampilkan semuaSalin
Ada tiga cara di mana suatu alamat dapat dizinkan untuk mentransfer token:
- Alamatnya adalah pemilik token
- Alamat disetujui untuk menggunakan token tersebut
- Alamat adalah operator untuk pemilik token
Fungsi di atas dapat merupakan suatu tampilan karena ia tidak mengubah state. Untuk mengurangi biaya pengoperasian, fungsi mana pun yang dapat berfungsi sebagai tampilan seharusnya menjadi tampilan.
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] -= 1Tampilkan semuaSalin
Saat ada masalah dengan transfer, kita membalikkan pemanggilan.
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_ADDRESSTampilkan semuaSalin
Hanya ubah nilainya jika diperlukan. Variabel state tinggal di penyimpanan. Menulis penyimpanan adalah salah satu operasi yang paling mahal yang dilakukan EVM (Mesin Virtual Ethereum) (jika dilhat dari penggunaan gas). Oleh karena itu, adalah ide bagus untuk meminimalkannya, bahkan menulis nilai yang sudah ada memakan biaya yang besar.
1@internal2def _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 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 """Tampilkan semuaSalin
Kita memiliki fungsi internal ini karena ada dua cara untuk mentransfer token (reguler dan aman), tetapi kita hanya ingin melakukannya di satu lokasi dalam kode di mana kita melakukannya untuk membuat proses audit menjadi lebih mudah.
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)Tampilkan semuaSalin
Untuk memancarkan aksi dalam Vyper, Anda menggunakan pernyataan log
(lihat di sini untuk selengkapnya(opens in a new tab)).
Fungsi Transfer
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)Tampilkan semuaSalin
Fungsi ini memungkinkan Anda mentransfer ke alamat arbitrari. Kecuali alamatnya adalah seorang pengguna, atau kontrak yang mengetahui cara mentransfer token, token mana pun yang Anda transfer akan terjebak dalam alamat tersebut dan menjadi tidak berguna.
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)Tampilkan semuaSalin
Tidak apa-apa melakukan transfer terlebih dahulu karena lagipula jika ada masalah, kita akan membalikkannya, sehingga semua hal yang dilakukan dalam pemanggilan akan dibatalkan.
1 if _to.is_contract: # check if `_to` is a contract addressSalin
Pertama-tama, periksa untuk melihat apakah alamatnya adalah suatu kontrak (jika ia memiliki kode). Jika tidak, anggaplah itu adalah alamat pengguna dan penggunanya akan dapat menggunakan token atau mentransfernya. Tetapi, jangan biarkan itu membuat Anda menjadi lengah karena rasa aman yang palsu. Anda dapat kehilangan token, bahkan dengan safeTransferFrom
, jika Anda mentransfer mereka ke suatu alamat yang kunci privatnya tidak diketahui siapa pun.
1 returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)Salin
Panggil kontrak target untuk melihat apakah ia dapat menerima 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)Salin
Jika tujuannya adalah suatu kontrak, tetapi itu tidak menerima token ERC-721 (atau itu memutuskan untuk tidak menerima transfer tertentu ini), balikkan.
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 != ownerTampilkan semuaSalin
Berdasarkan konvensi jika Anda tidak ingin memiliki pemberi persetujuan, maka Anda akan menugaskan ke alamat kosong, bukan ke alamat diri Anda sendiri.
1 # Check requirements2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender3 senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]4 assert (senderIsOwner or senderIsApprovedForAll)Salin
Untuk menetapkan persetujuan, Anda dapat menjadi pemiliknya, atau operator yang diotorisasi oleh pemilik.
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)Tampilkan semuaSalin
Cetak Token Baru dan Hancurkan Token Yang Sudah Ada
Akun yang membuat kontrak adalah minter
, pengguna super yang diotorisasi untuk mencetak NFT baru. Namun, bahkan pencetak tidak diizinkan untuk membakar token yang sudah ada. Hanya pemiliklah, atau entitas yang diotorisasi oleh pemilik, yang dapat melakukannya.
1### MINT & BURN FUNCTIONS ###23@external4def mint(_to: address, _tokenId: uint256) -> bool:Salin
Fungsi ini selalu mengembalikan True
, karena jika operasinya gagal maka ia akan dibalikkan.
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.minterTampilkan semuaSalin
Hanya pencetak (akun yang membuat kontrak ERC-721) yang dapat mencetak token baru. Ini dapat menjadi masalah di kemudian hari jika kita ingin mengubah identitas pencetak. Dalam kontrak produksi, Anda mungkin menginginkan fungsi yang memungkinkan pencetak mentransfer hak-hak istimewa pencetak kepada orang lain.
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 TrueSalin
Berdasarkan konvensi, pencetakan token baru dihitung sebagai sebuah transfer dari alamat nol.
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)Tampilkan semuaSalin
Siapa pun yang diizinkan untuk mentransfer sebuah token diizinkan untuk membakarnya. Sekalipun pembakaran tampak sama dengan transfer ke alamat kosong, alamat nolnya tidak benar-benar menerima token. Ini memungkinkan kita untuk membebaskan semua penyimpanan yang digunakan untuk token, yang dapat mengurangi biaya gas transaksi.
Menggunakan Kontrak ini
Berlawanan dengan Solidity, Vyper tidak memiliki warisan. Ini adalah pilihan rancangan yang disengaja untuk membuat kode lebih jelas dan karena itu lebih mudah untuk diamankan. Jadi, untuk membuat kontrak ERC-721 Vyper Anda, Anda mengambil kontrak ini(opens in a new tab) dan memodifikasinya untuk mengimplementasikan logika bisnis yang Anda inginkan.
Kesimpulan
Sebagai tinjauan, berikut adalah beberapa dari pokok pikiran terpenting dalam kontrak ini:
- Untuk menerima token ERC-721 dengan transfer yang aman, kontrak harus mengimplementasikan antarmuka
ERC721Receiver
. - Bahkan jika Anda menggunakan transfer yang aman, token masih dapat terjebak jika Anda mengirimkannya ke alamat yang kunci privatnya tidak diketahui.
- Saat ada masalah dengan suatu operasi, adalah ide bagus untuk
revert
pemanggilan, ketimbang hanya mengembalikan nilai gagal. - Token-token ERC-721 ada saat memiliki pemilik.
- Ada tiga cara untuk memiliki izin mentransfer suatu NFT. Anda dapat menjadi pemilik, disetujui untuk token tertentu, atau menjadi operator untuk semua token pemilik.
- Aksi lampau hanya terlihat dari luar blockchain. Kode yang beroperasi dalam blockchain tidak dapat melihat mereka.
Sekarang buat dan implementasikan kontrak Vyper yang aman.
Terakhir diedit: @nhsz(opens in a new tab), 15 Agustus 2023