Vyper ERC-721 Sözleşmesine Genel Bakış
Giriş
ERC-721 standardı, Değiştirilemez Jetonların (NFT) sahipliğini tutmak için kullanılır. ERC-20 jetonları, bireysel jetonlar arasında bir fark olmadığı için bir emtia gibi davranır. Bunun aksine, ERC-721 jetonları, farklı kedi çizgi filmleri veya farklı gayrimenkullerin tapuları gibi benzer ancak birebir aynı olmayan varlıklar için tasarlanmıştır.
Bu makalede Ryuya Nakamura'nın ERC-721 sözleşmesini (opens in a new tab) analiz edeceğiz. Bu sözleşme, güvensiz kod yazmayı Solidity'de olduğundan daha zorlaştırmak için tasarlanmış Python benzeri bir sözleşme dili olan Vyper (opens in a new tab) ile yazılmıştır.
Sözleşme
1# @dev ERC-721 değiştirilemez jeton standardının uygulaması.2# @yazar Ryuya Nakamura (@nrryuya)3# Şuradan değiştirildi: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vyPython'da olduğu gibi Vyper'da da yorumlar bir kare işareti (#) ile başlar ve satırın sonuna kadar devam eder. @<anahtar kelime> içeren yorumlar, NatSpec (opens in a new tab) tarafından insanlar tarafından okunabilir belgeler oluşturmak için kullanılır.
1from vyper.interfaces import ERC72123implements: ERC721ERC-721 arayüzü, Vyper dilinde yerleşiktir. Kod tanımını burada görebilirsiniz (opens in a new tab). Arayüz tanımı Vyper yerine Python'da yazılmıştır, çünkü arayüzler yalnızca blokzincir içinde değil, blokzincire Python ile yazılabilen harici bir istemciden bir işlem gönderilirken de kullanılır.
İlk satır, arayüzü içe aktarır ve ikincisi onu burada uyguladığımızı belirtir.
ERC721Receiver Arayüzü
1# safeTransferFrom() tarafından çağrılan sözleşme için arayüz2interface ERC721Receiver:3 def onERC721Received(ERC-721 iki tür aktarımı destekler:
transferFrom, göndericinin herhangi bir hedef adresi belirtmesine olanak tanır ve aktarım sorumluluğunu göndericiye yükler. Bu, geçersiz bir adrese aktarım yapabileceğiniz anlamına gelir, bu durumda NFT tamamen kaybolur.safeTransferFrom, hedef adresin bir sözleşme olup olmadığını kontrol eder. Eğer öyleyse, ERC-721 sözleşmesi alıcı sözleşmeye NFT'yi almak isteyip istemediğini sorar.
safeTransferFrom isteklerine yanıt vermek için alıcı bir sözleşmenin ERC721Receiver'ı uygulaması gerekir.
1 _operator: address,2 _from: address,_from adresi jetonun mevcut sahibidir. _operator adresi, aktarımı talep eden adrestir (ödenekler nedeniyle bu ikisi aynı olmayabilir).
1 _tokenId: uint256,ERC-721 jeton ID'leri 256 bittir. Tipik olarak, jetonun temsil ettiği şeyin bir açıklamasının hash edilmesiyle oluşturulurlar.
1 _data: Bytes[1024]İstek, 1024 bayta kadar kullanıcı verisine sahip olabilir.
1 ) -> bytes32: viewBir sözleşmenin yanlışlıkla bir aktarımı kabul ettiği durumları önlemek için, dönüş değeri bir boole değil, belirli bir değere sahip 256 bittir.
Bu işlev bir view'dur, yani blokzincirin durumunu okuyabilir, ancak değiştiremez.
Olaylar
Olaylar (opens in a new tab) blokzincir dışındaki kullanıcıları ve sunucuları olaylar hakkında bilgilendirmek için yayınlanır. Olayların içeriğinin blokzincirdeki sözleşmeler için mevcut olmadığını unutmayın.
1# @dev Herhangi bir NFT'nin mülkiyeti herhangi bir mekanizma ile değiştiğinde yayınlanır. Bu olay, NFT'ler2# oluşturulduğunda (`from` == 0) ve yok edildiğinde (`to` == 0) yayınlanır. İstisna: sözleşme oluşturma sırasında, herhangi3# bir sayıda NFT, Transfer yayınlanmadan oluşturulabilir ve atanabilir. Herhangi bir4# aktarım sırasında, o NFT için onaylanmış adres (varsa) sıfırlanır.5# @param _from NFT'nin göndericisi (adres sıfır adresi ise jeton oluşturmayı belirtir).6# @param _to NFT'nin alıcısı (adres sıfır adresi ise jeton yok etmeyi belirtir).7# @param _tokenId Aktarılan NFT.8event Transfer:9 sender: indexed(address)10 receiver: indexed(address)11 tokenId: indexed(uint256)Tümünü gösterBu, bir miktar yerine bir tokenId bildirmemiz dışında, ERC-20 Transfer olayına benzer.
Hiç kimse sıfır adresine sahip değildir, bu nedenle geleneksel olarak onu jetonların oluşturulmasını ve yok edilmesini bildirmek için kullanırız.
1# @dev Bu, bir NFT için onaylanmış adres değiştirildiğinde veya yeniden onaylandığında yayınlanır. Sıfır2# adresi, onaylanmış bir adres olmadığını gösterir. Bir Transfer olayı yayınlandığında, bu aynı zamanda3# o NFT için onaylanmış adresin (varsa) sıfırlandığını gösterir.4# @param _owner NFT'nin sahibi.5# @param _approved Onayladığımız adres.6# @param _tokenId Onayladığımız NFT.7event Approval:8 owner: indexed(address)9 approved: indexed(address)10 tokenId: indexed(uint256)Tümünü gösterERC-721 onayı, ERC-20 ödeneğine benzer. Belirli bir adresin belirli bir jetonu aktarmasına izin verilir. Bu, sözleşmelerin bir jetonu kabul ettiklerinde yanıt vermeleri için bir mekanizma sağlar. Sözleşmeler olayları dinleyemez, bu nedenle jetonu onlara aktarırsanız bunu "bilmezler". Bu şekilde, mal sahibi önce bir onay gönderir ve ardından sözleşmeye bir istek gönderir: "X jetonunu aktarmanız için onay verdim, lütfen yapın ...".
Bu, ERC-721 standardını ERC-20 standardına benzer kılmak için yapılmış bir tasarım tercihidir. ERC-721 jetonları değiştirilemez olduğundan, bir sözleşme, jetonun mülkiyetine bakarak belirli bir jeton aldığını da belirleyebilir.
1# @dev Bu, bir operatör bir sahip için etkinleştirildiğinde veya devre dışı bırakıldığında yayınlanır. Operatör, sahibinin tüm NFT'lerini2# yönetebilir.3# @param _owner NFT'nin sahibi.4# @param _operator Operatör haklarını ayarladığımız adres.5# @param _approved Operatör haklarının durumu (operatör hakları verilmişse true, geri alınmışsa6# false).7event ApprovalForAll:8 owner: indexed(address)9 operator: indexed(address)10 approved: boolTümünü gösterBir hesabın, adeta bir vekaletname gibi belirli bir türdeki (belirli bir sözleşmeyle yönetilenler) tüm jetonlarını yönetebilen bir operatöre sahip olmak bazen yararlıdır. Örneğin, altı aydır temas kurup kurmadığımı kontrol eden ve kurmadıysam varlıklarımı mirasçılarıma dağıtan bir sözleşmeye böyle bir yetki vermek isteyebilirim (mirasçılardan biri talep ederse, sözleşmeler bir işlemle çağrılmadan hiçbir şey yapamaz). ERC-20'de bir miras sözleşmesine sadece yüksek bir ödenek verebiliriz ancak bu, ERC-721 için işe yaramaz çünkü jetonlar değiştirilemezdir. Bu, bunun dengidir.
approved değeri bize etkinliğin bir onay için mi yoksa bir onayın geri çekilmesi için mi olduğunu söyler.
Durum Değişkenleri
Bu değişkenler, jetonların mevcut durumunu içerir: hangilerinin mevcut olduğu ve onlara kimin sahip olduğu. Bunların çoğu, iki tür arasında var olan tek yönlü eşlemeler (opens in a new tab) olan HashMap nesneleridir.
1# @dev NFT ID'sinden sahibinin adresine eşleme.2idToOwner: HashMap[uint256, address]34# @dev NFT ID'sinden onaylanan adrese eşleme.5idToApprovals: HashMap[uint256, address]Ethereum'daki kullanıcı ve sözleşme kimlikleri 160 bitlik adreslerle temsil edilir. Bu iki değişken, jeton ID'lerinden sahiplerine ve bunları aktarmak için onaylananlara eşleştirilir (her biri için en fazla bir tane). Ethereum'da, başlatılmamış veriler her zaman sıfırdır, bu nedenle herhangi bir sahip veya onaylanmış aktarıcı yoksa, o jetonun değeri sıfırdır.
1# @dev Sahip adresinden sahip olduğu jeton sayısına eşleme.2ownerToNFTokenCount: HashMap[address, uint256]Bu değişken, her sahip için jeton sayısını tutar. Sahiplerden jetonlara eşleştirme yoktur, bu nedenle belirli bir sahibin sahip olduğu jetonları tanımlamanın tek yolu blokzincirin olay geçmişine bakmak ve uygun Transfer olaylarını görmektir. Bu değişkeni, tüm NFT'lere ne zaman sahip olduğumuzu ve zamanda daha fazla aramamıza gerek olmadığını bilmek için kullanabiliriz.
Bu algoritmanın yalnızca kullanıcı arayüzleri ve harici sunucular için çalıştığını unutmayın. Blokzincirinin kendisinde çalışan kod geçmiş olayları okuyamaz.
1# @dev Sahip adresinden operatör adreslerinin eşlemesine eşleme.2ownerToOperators: HashMap[address, HashMap[address, bool]]Bir hesap birden fazla operatöre sahip olabilir. Basit bir HashMap onları takip etmek için yetersizdir, çünkü her anahtar tek bir değere bağlıdır. Bunun yerine, değer olarak HashMap[address, bool] kullanabilirsiniz. Varsayılan olarak, her adresin değeri False'dur, bu da bir operatör olmadığı anlamına gelir. Değerleri gerektiği gibi True olarak ayarlayabilirsiniz.
1# @dev Bir jeton basabilen minter'ın adresi2minter: addressYeni jetonlar bir şekilde oluşturulmalıdır. Bu sözleşmede bunu yapmasına izin verilen tek bir varlık vardır, minter. Bu, örneğin bir oyun için yeterli olabilir. Diğer amaçlar için daha karmaşık bir iş mantığı oluşturmak gerekebilir.
1# @dev Arayüz kimliğinden desteklenip desteklenmediğine dair bool'a eşleme2supportedInterfaces: HashMap[bytes32, bool]34# @dev ERC165'in ERC165 arayüzü ID'si5ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a767# @dev ERC721'in ERC165 arayüzü ID'si8ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cdERC-165 (opens in a new tab) bir sözleşmenin, uygulamaların onunla nasıl iletişim kurabileceğini, yani hangi ERC'lere uyduğunu açıklaması için bir mekanizma belirtir. Bu durumda sözleşme ERC-165 ve ERC-721'e uygundur.
Fonksiyonlar
Bunlar, ERC-721'i gerçekten uygulayan fonksiyonlardır.
Yapıcı
1@external2def __init__():Vyper'da, Python'da olduğu gibi, yapıcı fonksiyona __init__ adı verilir.
1 """2 @dev Sözleşme yapıcısı.3 """Python'da ve Vyper'da, çok satırlı bir dize (""" ile başlayan ve biten) belirterek ve onu hiçbir şekilde kullanmayarak bir yorum oluşturabilirsiniz. Bu yorumlar NatSpec (opens in a new tab) de içerebilir.
1 self.supportedInterfaces[ERC165_INTERFACE_ID] = True2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True3 self.minter = msg.senderDurum değişkenlerine erişmek için self.<değişken adı> kullanırsınız` (yine Python'daki gibi).
Görünüm Fonksiyonları
Bunlar blokzincirin durumunu değiştirmeyen fonksiyonlardır ve bu nedenle dışarıdan çağrıldıklarında ücretsiz olarak yürütülebilirler. Görünüm fonksiyonları bir sözleşme ile çağrılırsa, yine de her düğümde yürütülmeleri gerekir ve bu nedenle gaz harcarlar.
1@view2@externalBir at işareti (@) ile başlayan bir fonksiyon tanımından önceki bu anahtar kelimelere dekoratörler denir. Bir fonksiyonun çağrılabileceği durumları belirtirler.
@viewbu fonksiyonun bir görünüm olduğunu belirtir.@externalbu fonksiyonun işlemler ve diğer sözleşmeler tarafından çağrılabileceğini belirtir.
1def supportsInterface(_interfaceID: bytes32) -> bool:Python'un aksine Vyper statik tipli bir dildir (opens in a new tab).
veri türünü (opens in a new tab) belirtmeden bir değişken veya fonksiyon parametresi bildiremezsiniz. Bu durumda giriş parametresi, 256 bitlik bir değer olan bytes32'dir
(256 bit, Ethereum Sanal Makinesi'nin doğal kelime boyutudur). Çıktı bir boole
değeridir. Kural olarak, fonksiyon parametrelerinin adları bir alt çizgi (_) ile başlar.
1 """2 @dev Arayüz kimliği ERC-165'te belirtilmiştir.3 @param _interfaceID Arayüzün kimliği4 """5 return self.supportedInterfaces[_interfaceID]Değeri, yapıcıda (__init__) belirlenmiş olan self.supportedInterfaces HashMap'inden döndürün.
1### GÖRÜNÜM FONKSİYONLARI ###2Bunlar, jetonlar hakkında bilgileri kullanıcılara ve diğer sözleşmelere sunan görünüm fonksiyonlarıdır.
1@view2@external3def balanceOf(_owner: address) -> uint256:4 """5 @dev `_owner`'ın sahip olduğu NFT sayısını döndürür.6 `_owner` sıfır adresi ise hata verir. Sıfır adresine atanan NFT'ler geçersiz kabul edilir.7 @param _owner Bakiyenin sorgulanacağı adres.8 """9 assert _owner != ZERO_ADDRESSTümünü gösterBu satır, _owner'ın sıfır olmadığını denetler (opens in a new tab). Eğer öyleyse, bir hata vardır ve işlem geri alınır.
1@view2@external3def ownerOf(_tokenId: uint256) -> address:4 """5 @dev NFT'nin sahibinin adresini döndürür.6 `_tokenId` geçerli bir NFT değilse hata verir.7 @param _tokenId Bir NFT'nin tanımlayıcısı.8 """9 owner: address = self.idToOwner[_tokenId]10 # `_tokenId` geçerli bir NFT değilse hata verir11 assert owner != ZERO_ADDRESS12 return ownerTümünü gösterEthereum Sanal Makinesinde (EVM) içinde depolanmış bir değeri olmayan herhangi bir depolama sıfırdır.
Eğer _tokenId yerinde bir jeton yoksa self.idToOwner[_tokenId] değeri sıfırdır. Bu
durumda fonksiyon geri alınır.
1@view2@external3def getApproved(_tokenId: uint256) -> address:4 """5 @dev Tek bir NFT için onaylanmış adresi alın.6 `_tokenId` geçerli bir NFT değilse hata verir.7 @param _tokenId Onayını sorgulamak için NFT'nin ID'si.8 """9 # `_tokenId` geçerli bir NFT değilse hata verir10 assert self.idToOwner[_tokenId] != ZERO_ADDRESS11 return self.idToApprovals[_tokenId]Tümünü göstergetApproved'un sıfır döndürebileceğini unutmayın. Eğer jeton geçerliyse self.idToApprovals[_tokenId] döndürür.
Onaylayan yoksa bu değer sıfırdır.
1@view2@external3def isApprovedForAll(_owner: address, _operator: address) -> bool:4 """5 @dev `_operator`'ın `_owner` için onaylı bir operatör olup olmadığını kontrol eder.6 @param _owner NFT'lerin sahibi olan adres.7 @param _operator Sahip adına hareket eden adres.8 """9 return (self.ownerToOperators[_owner])[_operator]Tümünü gösterBu fonksiyon, _operator'un bu sözleşmedeki tüm _owner jetonlarını yönetmesine izin verilip verilmediğini kontrol eder.
Birden fazla operatör olabileceğinden, bu iki seviyeli bir HashMap'tir.
Aktarım Yardımcı Fonksiyonları
Bu fonksiyonlar, jetonları aktarmanın veya yönetmenin parçası olan işlemleri uygular.
12### AKTARIM FONKSİYONU YARDIMCILARI ###34@view5@internalBu dekoratör, @internal, fonksiyonun yalnızca aynı sözleşme içindeki diğer fonksiyonlardan erişilebilir olduğu anlamına gelir. Kural olarak, bu fonksiyon adları ayrıca bir alt çizgi (_) ile başlar.
1def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool:2 """3 @dev Verilen harcayıcının belirli bir jeton kimliğini aktarıp aktaramayacağını döndürür4 @param spender sorgulanacak harcayıcının adresi5 @param tokenId aktarılacak jetonun uint256 ID'si6 @return bool msg.sender'ın verilen jeton ID'si için onaylanıp onaylanmadığını,7 sahibin bir operatörü olup olmadığını veya jetonun sahibi olup olmadığını belirtir8 """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 spenderIsApprovedForAllTümünü gösterBir adresin bir jetonu aktarmasına izin verilmesinin üç yolu vardır:
- Adres, jetonun sahibidir
- Adresin bu jetonu harcaması onaylanmıştır
- Adres, jetonun sahibi için bir operatördür
Durumu değiştirmediği için yukarıdaki fonksiyon bir görünüm olabilir. İşletim maliyetlerini azaltmak için, görünüm olabilen herhangi bir fonksiyon görünüm olmalıdır.
1@internal2def _addTokenTo(_to: address, _tokenId: uint256):3 """4 @dev Belirli bir adrese bir NFT ekle5 `_tokenId`'nin bir sahibi varsa hata verir.6 """7 # `_tokenId`'nin bir sahibi varsa hata verir8 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 Belirli bir adresten bir NFT'yi kaldır19 `_from` mevcut sahip değilse hata verir.20 """21 # `_from` mevcut sahip değilse hata verir22 assert self.idToOwner[_tokenId] == _from23 # Change the owner24 self.idToOwner[_tokenId] = ZERO_ADDRESS25 # Change count tracking26 self.ownerToNFTokenCount[_from] -= 1Tümünü gösterAktarım ile ilgili bir sorun olduğunda çağrıyı geri alırız.
1@internal2def _clearApproval(_owner: address, _tokenId: uint256):3 """4 @dev Belirli bir adresin onayını temizle5 `_owner` mevcut sahip değilse hata verir.6 """7 # `_owner` mevcut sahip değilse hata verir8 assert self.idToOwner[_tokenId] == _owner9 if self.idToApprovals[_tokenId] != ZERO_ADDRESS:10 # Reset approvals11 self.idToApprovals[_tokenId] = ZERO_ADDRESSTümünü gösterDeğeri sadece gerekirse değiştirin. Durum değişkenleri depolamada yaşar. Depolama alanına yazmak, EVM'nin (Ethereum Sanal Makinesi) gerçekleştirdiği en pahalı işlemlerden biridir (gaz açısından). Bu nedenle en aza indirmek iyi bir fikirdir, mevcut değeri yazmanın bile maliyeti yüksektir.
1@internal2def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address):3 """4 @dev Bir NFT'nin aktarımını gerçekleştirin.5 `msg.sender` mevcut sahip, yetkili bir operatör veya bu NFT için onaylanmış6 adres değilse hata verir. (NOT: `msg.sender`'a özel fonksiyonda izin verilmez, bu yüzden `_sender`'ı geçin.)7 `_to` sıfır adresi ise hata verir.8 `_from` mevcut sahip değilse hata verir.9 `_tokenId` geçerli bir NFT değilse hata verir.10 """Tümünü gösterJetonları aktarmanın iki yolu olduğu için (normal ve güvenli) bu dahili fonksiyona sahibiz ancak denetimi kolaylaştırmak için kodda yalnızca tek bir konum istiyoruz.
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)Tümünü gösterVyper'da bir olay yayınlamak için bir log ifadesi kullanırsınız (daha fazla ayrıntı için buraya bakın (opens in a new tab)).
Aktarım Fonksiyonları
12### AKTARIM FONKSİYONLARI ###34@external5def transferFrom(_from: address, _to: address, _tokenId: uint256):6 """7 @dev `msg.sender` mevcut sahip, yetkili bir operatör veya bu NFT için onaylanmış8 adres değilse hata verir.9 `_from` mevcut sahip değilse hata verir.10 `_to` sıfır adresi ise hata verir.11 `_tokenId` geçerli bir NFT değilse hata verir.12 @notice Çağıran, `_to`'nun NFT'leri alabileceğinden emin olmaktan sorumludur, aksi takdirde13 kalıcı olarak kaybolabilirler.14 @param _from NFT'nin mevcut sahibi.15 @param _to Yeni sahip.16 @param _tokenId Aktarılacak NFT.17 """18 self._transferFrom(_from, _to, _tokenId, msg.sender)Tümünü gösterBu fonksiyon, isteğe bağlı bir adrese aktarım yapmanızı sağlar. Adres bir kullanıcı veya jetonların nasıl aktarılacağını bilen bir sözleşme olmadığı sürece, aktardığınız herhangi bir jeton o adreste takılıp kalır ve işe yaramaz hale gelir.
1@external2def safeTransferFrom(3 _from: address,4 _to: address,5 _tokenId: uint256,6 _data: Bytes[1024]=b""7 ):8 """9 @dev Bir NFT'nin mülkiyetini bir adresten başka bir adrese aktarır.10 `msg.sender` mevcut sahip, yetkili bir operatör veya bu NFT için11 onaylanmış adres değilse hata verir.12 `_from` mevcut sahip değilse hata verir.13 `_to` sıfır adresi ise hata verir.14 `_tokenId` geçerli bir NFT değilse hata verir.15 `_to` bir akıllı sözleşme ise, `_to` üzerinde `onERC721Received`'ı çağırır ve16 dönüş değeri `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` değilse hata verir.17 NOT: bytes4, dolgu ile bytes32 ile temsil edilir18 @param _from NFT'nin mevcut sahibi.19 @param _to Yeni sahip.20 @param _tokenId Aktarılacak NFT.21 @param _data Belirtilen bir formatı olmayan, `_to`'ya yapılan çağrıda gönderilen ek veriler.22 """23 self._transferFrom(_from, _to, _tokenId, msg.sender)Tümünü gösterÖnce transferi yapmakta bir sakınca yok çünkü bir sorun olursa yine de geri döneceğiz, bu yüzden çağrıda yapılan her şey iptal edilecek.
1 if _to.is_contract: # `_to`'nun bir sözleşme adresi olup olmadığını kontrol etİlk önce adresin bir sözleşme olup olmadığını kontrol edin (kodu varsa). Değilse, bunun bir kullanıcı adresi olduğunu
varsayın ve kullanıcı jetonu kullanabilecek veya aktarabilecektir. Ama bunun yüzünden yalancı bir
güvenlik duygusuna kapılmayın. Jetonları, özel anahtarı kimsenin bilmediği bir adrese aktarırsanız safeTransferFrom ile bile kaybedebilirsiniz.
1 returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)ERC-721 jetonlarını alıp alamayacağını görmek için hedef sözleşmeyi çağırın.
1 # Aktarım hedefi 'onERC721Received' uygulamayan bir sözleşme ise hata verir2 assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32)Hedef bir sözleşmeyse, ancak ERC-721 jetonlarını kabul etmeyen (veya bu özel aktarımı kabul etmemeye karar veren) bir sözleşmeyse, işlemi geri alın.
1@external2def approve(_approved: address, _tokenId: uint256):3 """4 @dev Bir NFT için onaylanmış adresi ayarlayın veya yeniden onaylayın. Sıfır adresi, onaylanmış bir adres olmadığını gösterir.5 `msg.sender` mevcut NFT sahibi veya mevcut sahibin yetkili bir operatörü değilse hata verir.6 `_tokenId` geçerli bir NFT değilse hata verir. (NOT: Bu, EIP'de yazılmamıştır)7 `_approved` mevcut sahip ise hata verir. (NOT: Bu, EIP'de yazılmamıştır)8 @param _approved Verilen NFT ID'si için onaylanacak adres.9 @param _tokenId Onaylanacak jetonun ID'si.10 """11 owner: address = self.idToOwner[_tokenId]12 # `_tokenId` geçerli bir NFT değilse hata verir13 assert owner != ZERO_ADDRESS14 # `_approved` mevcut sahip ise hata verir15 assert _approved != ownerTümünü gösterGeleneksel olarak, bir onaylayıcınız olmasını istemiyorsanız, kendinizi değil, sıfır adresini atarsınız.
1 # Check requirements2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender3 senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]4 assert (senderIsOwner or senderIsApprovedForAll)Bir onay ayarlamak için, sahibi veya sahibi tarafından yetkilendirilmiş bir operatör olabilirsiniz.
1 # Set the approval2 self.idToApprovals[_tokenId] = _approved3 log Approval(owner, _approved, _tokenId)456@external7def setApprovalForAll(_operator: address, _approved: bool):8 """9 @dev Üçüncü bir taraf ("operatör") için `msg.sender`'ın tüm varlıklarını yönetme onayını etkinleştirir veya devre dışı bırakır.10 Ayrıca ApprovalForAll olayını da yayınlar.11 `_operator`'ın `msg.sender` olması durumunda hata verir. (NOT: Bu, EIP'de yazılmamıştır)12 @notice Bu, göndericinin o anda hiçbir jetona sahip olmasa bile çalışır.13 @param _operator Yetkili operatörler kümesine eklenecek adres.14 @param _approved Operatör onaylanmışsa True, onayı iptal etmek için false.15 """16 # `_operator`'ın `msg.sender` olması durumunda hata verir17 assert _operator != msg.sender18 self.ownerToOperators[msg.sender][_operator] = _approved19 log ApprovalForAll(msg.sender, _operator, _approved)Tümünü gösterYeni Jetonlar Basma ve Mevcut Olanları Yok Etme
Sözleşmeyi oluşturan hesap, yeni NFT'leri basmaya yetkili süper kullanıcı olan minter'dır. Ancak, onun bile mevcut jetonları yakmasına izin verilmez. Bunu yalnızca mal sahibi veya mal sahibi tarafından yetkilendirilmiş bir varlık yapabilir.
1### BASMA VE YAKMA FONKSİYONLARI ###23@external4def mint(_to: address, _tokenId: uint256) -> bool:Bu fonksiyon her zaman True döndürür, çünkü işlem başarısız olursa geri alınır.
1 """2 @dev Jeton basma fonksiyonu3 `msg.sender` minter değilse hata verir.4 `_to` sıfır adresi ise hata verir.5 `_tokenId`'nin bir sahibi varsa hata verir.6 @param _to Basılan jetonları alacak olan adres.7 @param _tokenId Basılacak jeton kimliği.8 @return İşlemin başarılı olup olmadığını gösteren bir boole değeri.9 """10 # `msg.sender` minter değilse hata verir11 assert msg.sender == self.minterTümünü gösterYalnızca minter (ERC-721 sözleşmesini oluşturan hesap) yeni jetonlar basabilir. Bu, gelecekte minter'ın kimliğini değiştirmek istersek bir sorun yaratabilir. Bir üretim sözleşmesinde, muhtemelen minter'ın minter ayrıcalıklarını başka birine devretmesine izin veren bir fonksiyonun olmasını istersiniz.
1 # `_to` sıfır adresi ise hata verir2 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 TrueGeleneksel olarak, yeni jetonların basımı sıfır adresinden bir aktarım olarak sayılır.
12@external3def burn(_tokenId: uint256):4 """5 @dev Belirli bir ERC721 jetonunu yakar.6 `msg.sender` mevcut sahip, yetkili bir operatör veya bu NFT için onaylanmış7 adres değilse hata verir.8 `_tokenId` geçerli bir NFT değilse hata verir.9 @param _tokenId Yakılacak ERC721 jetonunun uint256 id'si.10 """11 # Check requirements12 assert self._isApprovedOrOwner(msg.sender, _tokenId)13 owner: address = self.idToOwner[_tokenId]14 # `_tokenId` geçerli bir NFT değilse hata verir15 assert owner != ZERO_ADDRESS16 self._clearApproval(owner, _tokenId)17 self._removeTokenFrom(owner, _tokenId)18 log Transfer(owner, ZERO_ADDRESS, _tokenId)Tümünü gösterBir jetonu aktarmasına izin verilen herkesin onu yakmasına izin verilir. Bir yakma işlemi, sıfır adresine aktarıma eş değer görünse de, sıfır adresi aslında jetonu almaz. Bu, jeton için kullanılan tüm depolama alanını boşaltmamızı sağlar ve bu da işlemin gaz maliyetini azaltabilir.
Bu Sözleşmeyi Kullanma
Solidity'nin aksine, Vyper'ın kalıtımı yoktur. Bu, kodu daha net hâle getirmek ve dolayısıyla güvenliğini sağlamak için bilinçli bir tasarım seçimidir. Yani kendi Vyper ERC-721 sözleşmenizi oluşturmak için bu sözleşmeyi alır ve istediğiniz iş mantığını uygulamak için değiştirirsiniz.
Sonuç
İnceleme için, bu sözleşmedeki en önemli fikirlerden bazıları şunlardır:
- ERC-721 jetonlarını güvenli bir aktarımla almak için sözleşmelerin
ERC721Receiverarayüzünü uygulaması gerekir. - Güvenli aktarım kullansanız bile, özel anahtarı bilinmeyen bir adrese gönderirseniz jetonlar takılıp kalabilir.
- Bir işlemle ilgili bir sorun olduğunda yalnızca bir hata değeri döndürmek yerine çağrıyı
geri almakiyi bir fikirdir. - ERC-721 jetonları, bir sahibi olduğunda var olurlar.
- Bir NFT'yi aktarma yetkisine sahip olmanın üç yolu vardır. Sahibi olabilir, belirli bir jeton için onay alabilir veya sahibinin tüm jetonları için operatör olabilirsiniz.
- Geçmiş olaylar sadece blokzincirin dışında görülebilir. Blokzincirin içinde çalışan kod onları göremez.
Artık güvenli Vyper sözleşmelerini uygulayabilirsiniz.
Çalışmalarımdan daha fazlası için buraya bakın (opens in a new tab).
Sayfanın son güncellenmesi: 22 Ağustos 2025