Ana içeriğe geç

Vyper ERC-721 Sözleşmesine Genel Bakış

vyper
erc-721
python
Acemi
Ori Pomerantz
1 Nisan 2021
16 dakikalık okuma

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.vy

Python'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 ERC721
2
3implements: ERC721

ERC-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üz
2interface 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: view

Bir 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'ler
2# oluşturulduğunda (`from` == 0) ve yok edildiğinde (`to` == 0) yayınlanır. İstisna: sözleşme oluşturma sırasında, herhangi
3# bir sayıda NFT, Transfer yayınlanmadan oluşturulabilir ve atanabilir. Herhangi bir
4# 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öster

Bu, 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ır
2# adresi, onaylanmış bir adres olmadığını gösterir. Bir Transfer olayı yayınlandığında, bu aynı zamanda
3# 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öster

ERC-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'lerini
2# 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ışsa
6# false).
7event ApprovalForAll:
8 owner: indexed(address)
9 operator: indexed(address)
10 approved: bool
Tümünü göster

Bir 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]
3
4# @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 adresi
2minter: address

Yeni 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şleme
2supportedInterfaces: HashMap[bytes32, bool]
3
4# @dev ERC165'in ERC165 arayüzü ID'si
5ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
6
7# @dev ERC721'in ERC165 arayüzü ID'si
8ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd

ERC-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@external
2def __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] = True
2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True
3 self.minter = msg.sender

Durum 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@view
2@external

Bir 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.

  • @view bu fonksiyonun bir görünüm olduğunu belirtir.
  • @external bu 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ği
4 """
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 ###
2

Bunlar, jetonlar hakkında bilgileri kullanıcılara ve diğer sözleşmelere sunan görünüm fonksiyonlarıdır.

1@view
2@external
3def 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_ADDRESS
Tümünü göster

Bu 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@view
2@external
3def 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 verir
11 assert owner != ZERO_ADDRESS
12 return owner
Tümünü göster

Ethereum 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@view
2@external
3def 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 verir
10 assert self.idToOwner[_tokenId] != ZERO_ADDRESS
11 return self.idToApprovals[_tokenId]
Tümünü göster

getApproved'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@view
2@external
3def 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öster

Bu 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.

1
2### AKTARIM FONKSİYONU YARDIMCILARI ###
3
4@view
5@internal

Bu 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ür
4 @param spender sorgulanacak harcayıcının adresi
5 @param tokenId aktarılacak jetonun uint256 ID'si
6 @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ı belirtir
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
Tümünü göster

Bir adresin bir jetonu aktarmasına izin verilmesinin üç yolu vardır:

  1. Adres, jetonun sahibidir
  2. Adresin bu jetonu harcaması onaylanmıştır
  3. 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@internal
2def _addTokenTo(_to: address, _tokenId: uint256):
3 """
4 @dev Belirli bir adrese bir NFT ekle
5 `_tokenId`'nin bir sahibi varsa hata verir.
6 """
7 # `_tokenId`'nin bir sahibi varsa hata verir
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 Belirli bir adresten bir NFT'yi kaldır
19 `_from` mevcut sahip değilse hata verir.
20 """
21 # `_from` mevcut sahip değilse hata verir
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
Tümünü göster

Aktarım ile ilgili bir sorun olduğunda çağrıyı geri alırız.

1@internal
2def _clearApproval(_owner: address, _tokenId: uint256):
3 """
4 @dev Belirli bir adresin onayını temizle
5 `_owner` mevcut sahip değilse hata verir.
6 """
7 # `_owner` mevcut sahip değilse hata verir
8 assert self.idToOwner[_tokenId] == _owner
9 if self.idToApprovals[_tokenId] != ZERO_ADDRESS:
10 # Reset approvals
11 self.idToApprovals[_tokenId] = ZERO_ADDRESS
Tümünü göster

Değ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@internal
2def _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öster

Jetonları 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 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)
Tümünü göster

Vyper'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ı

1
2### AKTARIM FONKSİYONLARI ###
3
4@external
5def 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 takdirde
13 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öster

Bu 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@external
2def 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çin
11 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 ve
16 dönüş değeri `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` değilse hata verir.
17 NOT: bytes4, dolgu ile bytes32 ile temsil edilir
18 @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 verir
2 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@external
2def 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 verir
13 assert owner != ZERO_ADDRESS
14 # `_approved` mevcut sahip ise hata verir
15 assert _approved != owner
Tümünü göster

Geleneksel olarak, bir onaylayıcınız olmasını istemiyorsanız, kendinizi değil, sıfır adresini atarsınız.

1 # Check requirements
2 senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender
3 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 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 Üçü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 verir
17 assert _operator != msg.sender
18 self.ownerToOperators[msg.sender][_operator] = _approved
19 log ApprovalForAll(msg.sender, _operator, _approved)
Tümünü göster

Yeni 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 ###
2
3@external
4def 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 fonksiyonu
3 `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 verir
11 assert msg.sender == self.minter
Tümünü göster

Yalnı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 verir
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

Geleneksel olarak, yeni jetonların basımı sıfır adresinden bir aktarım olarak sayılır.

1
2@external
3def 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 requirements
12 assert self._isApprovedOrOwner(msg.sender, _tokenId)
13 owner: address = self.idToOwner[_tokenId]
14 # `_tokenId` geçerli bir NFT değilse hata verir
15 assert owner != ZERO_ADDRESS
16 self._clearApproval(owner, _tokenId)
17 self._removeTokenFrom(owner, _tokenId)
18 log Transfer(owner, ZERO_ADDRESS, _tokenId)
Tümünü göster

Bir 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 ERC721Receiver arayü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 almak iyi 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

Bu rehber yararlı oldu mu?