Ana içeriğe geç

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

vypererc-721python
Acemi
Ori Pomerantz
1 Nisan 2021
17 dakikalık okuma minute read

Giriş

ERC-721 standardı, Değiştirilemez Token'ların (NFT) sahipliğini tutmak için kullanılır. ERC-20 token'ları, bireysel token'lar arasında bir fark olmadığı için bir emtia gibidir. Bunun aksine ERC-721 token'ları, farklı kedi karikatürleri(opens in a new tab) veya farklı gayrimenkul parçalarına verilen unvanlar gibi benzer ancak 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 dizayn edilmiş Python benzeri bir sözleşme dili olan Vyper(opens in a new tab) ile yazılmıştır.

Sözleşme

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
Kopyala

Python'da olduğu gibi Vyper'da da yorumlar bir hash (#) ile başlar ve satırın sonuna kadar devam eder. @<keyword> içeren yorumlar insan tarafından okunabilir belgeler oluşturmak için NatSpec(opens in a new tab) tarafından kullanılır.

1from vyper.interfaces import ERC721
2
3implements: ERC721
Kopyala

ERC-721 arayüzü, Vyper dilinde yerleşiktir. Kod tanımlaması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 blok zinciri içinde değil, blok zincirine Python'da yazılabilen harici bir istemciden bir işlem gönderirken 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# Interface for the contract called by safeTransferFrom()
2interface ERC721Receiver:
3 def onERC721Received(
Kopyala

ERC-721 iki tür aktarımı destekler:

  • transferFrom, gönderenin herhangi bir hedef adresi belirlemesine izin verir ve transfer sorumluluğunu gönderene yükler. Bu, geçersiz bir adrese transfer yapabileceğiniz anlamına gelir, bu durumda NFT tamamen kaybolur.
  • Hedef adresin bir sözleşme olduğunu kontrol eden safeTransferFrom. Eğer öyleyse, ERC-721 sözleşmesi alıcı sözleşmeye NFT'yi almak isteyip istemediğini sorar.

safeTransferFrom isteklerini yanıtlamak için bir alıcı sözleşmesinin ERC721Receiver uygulaması gerekir.

1 _operator: address,
2 _from: address,
Kopyala

_from adresi token'ın mevcut sahibidir. _operator adresi, transferi talep eden adrestir (ödenekler nedeniyle bu ikisi aynı olmayabilir).

1 _tokenId: uint256,
Kopyala

ERC-721 token ID'leri 256 bittir. Tipik olarak, token'ın temsil ettiği şeyin bir açıklamasının hash edilmesiyle oluşturulurlar.

1 _data: Bytes[1024]
Kopyala

İstek, 1024 bayta kadar kullanıcı verisine sahip olabilir.

1 ) -> bytes32: view
Kopyala

Bir sözleşmenin yanlışlıkla bir transferi kabul ettiği durumları önlemek için, dönüş değeri bir boolean değil, belirli bir değere sahip 256 bittir.

Bu işlev bir view'dur, yani blok zincirinin durumunu okuyabilir, ancak değiştiremez.

Etkinlikler

Olaylar(opens in a new tab) blok zincirinin dışındaki kullanıcıları ve sunucuları bilgilendirmek için yayınlanır. Olayların içeriğinin blok zincirindeki sözleşmeler için mevcut olmadığını unutmayın.

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 transferred.
8event Transfer:
9 sender: indexed(address)
10 receiver: indexed(address)
11 tokenId: indexed(uint256)
Tümünü göster
Kopyala

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 token'ların oluşturulmasını ve yok edilmesini bildirmek için kullanırız.

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

ERC-721 onayı, ERC-20 ödeneğine benzer. Belirli bir adresin belirli bir token'ı aktarmasına izin verilir. Bu, sözleşmelerin bir token'ı kabul ettiklerinde yanıt vermeleri için bir mekanizma sağlar. Sözleşmeler olayları dinleyemez, bu nedenle token'ı 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 token'ını 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 token'ları değiştirilemez olduğundan, bir sözleşme token'ın mülkiyetine bakarak belirli bir token aldığını da belirleyebilir.

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
Tümünü göster
Kopyala

Bir hesabın, adeta bir vekaletname gibi belirli bir türdeki (belirli bir sözleşmeyle yönetilenler) tüm simgelerini yönetebilen bir operatöre sahip olmak bazen yararlıdır. Örneğin, altı aydır iletişime geçmediğimi kontrol eden ve varsa mal varlığımı mirasçılarıma dağıtan bir sözleşmeye öyle bir yetki vermek isteyebilirim (eğer bir sözleşme bunu isterse, bir işlem tarafından çağrılmadan herhangi 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ü token'lar değiştirilebilir değildir. 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, token'ları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şleştirmeler(opens in a new tab) olan HashMap nesneleridir.

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]
Kopyala

Ethereum'daki kullanıcı ve sözleşme kimlikleri 160 bitlik adreslerle temsil edilir. Bu iki değişken, token 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 token'ın değeri sıfırdır.

1# @dev Mapping from owner address to count of his tokens.
2ownerToNFTokenCount: HashMap[address, uint256]
Kopyala

Bu değişken, her sahip için token sayısını tutar. Sahiplerden token'lara eşleştirme yoktur, bu nedenle belirli bir sahibin sahip olduğu token'ları tanımlamanın tek yolu blok zincirinin 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. Blok zincirinde çalışan kod, geçmiş olayları okuyamaz.

1# @dev Mapping from owner address to mapping of operator addresses.
2ownerToOperators: HashMap[address, HashMap[address, bool]]
Kopyala

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 Address of minter, who can mint a token
2minter: address
Kopyala

Yeni token'lar 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 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
Kopyala

ERC-165(opens in a new tab), uygulamaların kendisiyle nasıl iletişim kurabileceğini ve hangi ERC'lere uyduğunu ifşa etmek için bir sözleşme 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__():
Kopyala

Vyper'da, Python'da olduğu gibi yapıcı fonksiyona __init__ adı verilir.

1 """
2 @dev Contract constructor.
3 """
Kopyala

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 ayrıca NatSpec(opens in a new tab) içerebilir.

1 self.supportedInterfaces[ERC165_INTERFACE_ID] = True
2 self.supportedInterfaces[ERC721_INTERFACE_ID] = True
3 self.minter = msg.sender
Kopyala

Durum değişkenlerine erişmek için self.<variable name> kullanırsınız (yine Python'da olduğu gibi).

Fonksiyonları gör

Bunlar blok zincirinin 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
Kopyala

Bir @ işaretiyle başlayan bir fonksiyon tanımından önceki bu anahtar kelimelere dekorasyon denir. Bir fonksiyonun çağrılabileceği durumları belirtirler.

  • @view bu fonksiyonun bir view 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:
Kopyala

Python'un aksine, Vyper statik türlendirilmiş bir dildir(opens in a new tab). Veri türünü(opens in a new tab) tanımlamadan 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 yerel kelime boyutudur). Çıktı boolean bir değerdir. Kural olarak, fonksiyon parametrelerinin adları bir alt çizgi (_) ile başlar.

1 """
2 @dev Interface identification is specified in ERC-165.
3 @param _interfaceID Id of the interface
4 """
5 return self.supportedInterfaces[_interfaceID]
Kopyala

Değeri, yapıcıda (__init__) belirlenmiş olan self.supportedInterfaces HashMap'inden döndürün.

1### VIEW FUNCTIONS ###
Kopyala

Bunlar, token'lar hakkında bilgileri kullanıcılara ve diğer sözleşmelere sunan görüntüleme fonksiyonlarıdır.

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
Tümünü göster
Kopyala

Bu satır _owner'ın sıfır olmadığını teyit eder(opens in a new tab). Eğer öyleyse, bir hata vardır ve işlem geri alınır.

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
Tümünü göster
Kopyala

Ethereum Sanal Makinesinde (evm), içinde depolanmış bir değeri olmayan herhangi bir depolama sıfırdır. Eğer _tokenId yerinde bir token yoksa self.idToOwner[_tokenId] değeri sıfırdır. Bu durumda fonksiyon geri dönüş yapar.

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]
Tümünü göster
Kopyala

getApproved'un sıfır döndürebileceğini unutmayın. Eğer token 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 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]
Tümünü göster
Kopyala

Bu fonksiyon, _operator'un bu sözleşmedeki tüm _owner token'larını yönetmesine izin verilip verilmediğini kontrol eder. Birden fazla operatör olabileceğinden, bu iki seviyeli bir HashMap'tir.

Transfer Yardımcı Fonksiyonları

Bu fonksiyonlar, token'ları transfer etmenin veya yönetmenin parçası olan işlemleri uygular.

1
2### TRANSFER FUNCTION HELPERS ###
3
4@view
5@internal
Kopyala

Bu dekorasyon, @internal, fonksiyona yalnızca aynı sözleşmedeki 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 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
Tümünü göster
Kopyala

Bir adresin bir token'ı transfer etmesine izin vermenin üç yolu vardır:

  1. Adres, token'ın sahibidir
  2. Adresin bu token'ı harcaması onaylanmıştır
  3. Adres, token'ın 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, bir görünüm olabilen herhangi bir fonksiyon bir görünüm olmalıdır.

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
Tümünü göster
Kopyala

Transfer ile ilgili bir sorun olduğunda isteği geri çeviriyoruz.

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
Tümünü göster
Kopyala

Değeri sadece gerekirse değiştirin. Durum değişkenleri depolamada yaşar. Depolamaya yazmak, EVM'nin (Ethereum Sanal Makinesi) yaptığı 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 Execute 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 """
Tümünü göster
Kopyala

Token'ları aktarmanın iki yolu olduğu için (düzenli 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
Kopyala

Vyper'da bir olay yaymak için log ifadesi kullanırsınız (daha fazla detay için buraya bakınız(opens in a new tab)).

Transfer Fonksiyonları

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

Bu fonksiyon, isteğe bağlı bir adrese aktarım yapmanızı sağlar. Adres bir kullanıcı veya token'ların nasıl transfer edileceğini bilen bir sözleşme olmadığı sürece, transfer ettiğiniz herhangi bir token o adrese takılıp işe yaramaz olacaktır.

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

Önce transferi yapmakta bir sakınca yok çünkü bir sorun olursa yine geri döneceğiz, bu yüzden çağrıda yapılan her şey iptal edilecek.

1 if _to.is_contract: # check if `_to` is a contract address
Kopyala

İ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ı token'ı kullanabilecek veya aktarabilecektir. Ama bunun yüzünden yalancı bir güvenlik duygusuna kapılmayın. Token'ları, ö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)
Kopyala

ERC-721 token'larını alıp alamayacağını görmek için hedef sözleşmeyi çağırın.

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)
Kopyala

Hedef bir sözleşmeyse, ancak ERC-721 token'larını kabul etmeyen (veya bu özel aktarımı kabul etmemeye karar veren) bir sözleşmeyse, geri döndürün.

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
Tümünü göster
Kopyala

Normalde, bir onaylayıcıya sahip olmak istemiyorsanız sıfır adresini kendiniz atmazsı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)
Kopyala

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

Yeni Token'lar 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 token'ları yakmasına izin verilmez. Bunu yalnızca mal sahibi veya mal sahibi tarafından yetkilendirilmiş bir varlık yapabilir.

1### MINT & BURN FUNCTIONS ###
2
3@external
4def mint(_to: address, _tokenId: uint256) -> bool:
Kopyala

Bu fonksiyon her zaman True döndürür, çünkü işlem başarısız olursa geri alınır.

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
Tümünü göster
Kopyala

Yalnızca "minter" (ERC-721 sözleşmesini oluşturan hesap) yeni token'lar basabilir. Bu, "minter"ın kimliğini değiştirmek istersek gelecekte 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 # 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
Kopyala

Geleneksel olarak, yeni token'ların basımı sıfır adresinden bir transfer olarak sayılır.

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

Bir token'ı transfer etmesine 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 token'ı almaz. Bu, token için kullanılan tüm depolama alanını boşaltmamızı sağlar ve bu da işlemin gasz maliyetini azaltabilir.

Bu Sözleşmeyi Kullanmak

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. Bu nedenle, kendi Vyper ERC-721 sözleşmenizi oluşturmak için bu sözleşmeyi(opens in a new tab) alın ve istediğiniz iş mantığını uygulamak için değiştirin.

Sonuç

İnceleme için, bu sözleşmedeki en önemli fikirlerden bazıları şunlardır:

  • ERC-721 token'larını güvenli bir aktarımla almak için sözleşmelerin ERC721Receiver arayüzünü uygulaması gerekir.
  • Güvenli transfer kullansanız bile, özel anahtarı bilinmeyen bir adrese gönderirseniz token'lar takılıp kalabilir.
  • Bir operasyonla ilgili bir sorun olduğunda çağrıyı revert etmek, bir başarısızlık değeri döndürmekten daha iyi bir fikirdir.
  • ERC-721 token'ları, bir sahibi olduğunda var olurlar.
  • Bir NFT'yi transfer etme yetkisine sahip olmanın üç yolu vardır. Sahibi olabilir, belirli bir token için onay alabilir veya sahibinin tüm token'ları için operatör olabilirsiniz.
  • Geçmiş olaylar sadece blok zincirinin dışında görülebilir. Blok zincirinin içinde çalışan kod onları göremez.

Artık güvenli Vyper sözleşmelerini uygulayabilirsiniz.

Son düzenleme: @nhsz(opens in a new tab), 15 Ağustos 2023

Bu rehber yararlı oldu mu?