Vyper ERC-721 కాంట్రాక్ట్ వాక్త్రూ
పరిచయం
నాన్-ఫంజిబుల్ టోకెన్ల (NFT) యాజమాన్యాన్ని కలిగి ఉండటానికి ERC-721 ప్రమాణం ఉపయోగించబడుతుంది. ERC-20 టోకెన్లు ఒక వస్తువు వలె ప్రవర్తిస్తాయి, ఎందుకంటే వ్యక్తిగత టోకెన్ల మధ్య ఎటువంటి తేడా ఉండదు. దానికి విరుద్ధంగా, ERC-721 టోకెన్లు ఒకేలా ఉండే కానీ ఖచ్చితంగా ఒకేలా ఉండని ఆస్తుల కోసం రూపొందించబడ్డాయి, ఉదాహరణకు విభిన్న పిల్లి కార్టూన్లు (కొత్త ట్యాబ్లో తెరవబడుతుంది) లేదా వివిధ రియల్ ఎస్టేట్ ఆస్తుల టైటిల్స్.
ఈ వ్యాసంలో మనం Ryuya Nakamura యొక్క ERC-721 కాంట్రాక్ట్ (కొత్త ట్యాబ్లో తెరవబడుతుంది)ను విశ్లేషిస్తాము. ఈ కాంట్రాక్ట్ Vyper (కొత్త ట్యాబ్లో తెరవబడుతుంది)లో వ్రాయబడింది, ఇది Solidityలో కంటే అసురక్షిత కోడ్ను వ్రాయడం కష్టతరం చేయడానికి రూపొందించబడిన Python లాంటి కాంట్రాక్ట్ భాష.
కాంట్రాక్ట్
# @dev ERC-721 నాన్-ఫంజిబుల్ టోకెన్ ప్రమాణం యొక్క అమలు.
# @author Ryuya Nakamura (@nrryuya)
# దీని నుండి సవరించబడింది: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy
Pythonలో వలె Vyperలో వ్యాఖ్యలు హాష్ (ethereum.ercs)తో ప్రారంభమవుతాయి మరియు లైన్ చివరి వరకు కొనసాగుతాయి. @<keyword>ని కలిగి ఉన్న వ్యాఖ్యలు మానవులు చదవగలిగే డాక్యుమెంటేషన్ను రూపొందించడానికి NatSpec (కొత్త ట్యాబ్లో తెరవబడుతుంది) ద్వారా ఉపయోగించబడతాయి.
from vyper.interfaces import ERC721
implements: ERC721
ERC-721 ఇంటర్ఫేస్ Vyper భాషలో నిర్మించబడింది. మీరు కోడ్ నిర్వచనాన్ని ఇక్కడ చూడవచ్చు (కొత్త ట్యాబ్లో తెరవబడుతుంది). ఇంటర్ఫేస్ నిర్వచనం Vyperకి బదులుగా Pythonలో వ్రాయబడింది, ఎందుకంటే ఇంటర్ఫేస్లు బ్లాక్చైన్లో మాత్రమే కాకుండా, బాహ్య క్లయింట్ నుండి బ్లాక్చైన్కు లావాదేవీని పంపుతున్నప్పుడు కూడా ఉపయోగించబడతాయి, ఇది Pythonలో వ్రాయబడి ఉండవచ్చు.
మొదటి లైన్ ఇంటర్ఫేస్ను దిగుమతి చేస్తుంది మరియు రెండవది మనం దానిని ఇక్కడ అమలు చేస్తున్నామని నిర్దేశిస్తుంది.
#pragma version >0.3.10
#pragma version >0.3.10
ERC721Receiver ఇంటర్ఫేస్
# safeTransferFrom() ద్వారా కాల్ చేయబడిన కాంట్రాక్ట్ కోసం ఇంటర్ఫేస్
interface ERC721Receiver:
def onERC721Received(
ERC-721 రెండు రకాల బదిలీలకు మద్దతు ఇస్తుంది:
transferFrom, ఇది పంపినవారు ఏదైనా గమ్యస్థాన చిరునామాను పేర్కొనడానికి అనుమతిస్తుంది మరియు బదిలీ బాధ్యతను పంపినవారిపై ఉంచుతుంది. దీని అర్థం మీరు చెల్లని చిరునామాకు బదిలీ చేయవచ్చు, ఆ సందర్భంలో NFT శాశ్వతంగా కోల్పోబడుతుంది.safeTransferFrom, ఇది గమ్యస్థాన చిరునామా కాంట్రాక్ట్ అవునా కాదా అని తనిఖీ చేస్తుంది. అలా అయితే, స్వీకరించే కాంట్రాక్ట్ NFTని స్వీకరించాలనుకుంటుందా అని ERC-721 కాంట్రాక్ట్ అడుగుతుంది.
safeTransferFrom అభ్యర్థనలకు సమాధానం ఇవ్వడానికి స్వీకరించే కాంట్రాక్ట్ ERC721Receiverని అమలు చేయాలి.
_operator: address,
_from: address,
_from చిరునామా అనేది టోకెన్ యొక్క ప్రస్తుత యజమాని. _operator చిరునామా అనేది బదిలీని అభ్యర్థించినది (అనుమతి మొత్తాల కారణంగా ఆ రెండూ ఒకేలా ఉండకపోవచ్చు). సాంప్రదాయం ప్రకారం, ఈ కాంట్రాక్ట్లోని చాలా ఫంక్షన్ పారామితులు అండర్స్కోర్ (_)తో ప్రారంభమవుతాయి.
_tokenId: uint256,
ERC-721 టోకెన్ IDలు 256 బిట్లు. సాధారణంగా టోకెన్ దేనిని సూచిస్తుందో దాని వివరణను హాషింగ్ చేయడం ద్వారా అవి సృష్టించబడతాయి.
_data: Bytes[1024]
అభ్యర్థన గరిష్టంగా 1024 బైట్ల వినియోగదారు డేటాను కలిగి ఉండవచ్చు.
) -> bytes4: nonpayable
కాంట్రాక్ట్ పొరపాటున బదిలీని అంగీకరించే సందర్భాలను నివారించడానికి, తిరిగి ఇచ్చే విలువ బూలియన్ కాదు, కానీ నిర్దిష్ట నాలుగు-బైట్ల విలువ, onERC721Received యొక్క ఫంక్షన్ సెలెక్టర్. ఫంక్షన్ nonpayable ఎందుకంటే స్వీకరించే కాంట్రాక్ట్ టోకెన్ను అంగీకరించినప్పుడు దాని స్వంత స్థితిని మార్చుకోవచ్చు.
ఈవెంట్లు
ఈవెంట్ల గురించి బ్లాక్చైన్ వెలుపల ఉన్న వినియోగదారులకు మరియు సర్వర్లకు తెలియజేయడానికి ఈవెంట్లు విడుదల చేయబడతాయి. ఈవెంట్ల కంటెంట్ బ్లాక్చైన్లోని కాంట్రాక్ట్లకు అందుబాటులో ఉండదని గమనించండి. మూడు ERC-721 ఈవెంట్లు మనం దిగుమతి చేసుకున్న IERC721 ఇంటర్ఫేస్ ద్వారా నిర్వచించబడ్డాయి, కాబట్టి ఈ కాంట్రాక్ట్ వాటిని స్వయంగా ప్రకటించదు; దిగువ బదిలీ ఫంక్షన్లలో మనం చూసే విధంగా, ఇది వాటిని log IERC721.<Event>(...)తో విడుదల చేస్తుంది.
Transfer (sender, receiver, token_id) అనేది NFT యాజమాన్యంలో మార్పును నివేదిస్తుంది. ఇది ERC-20 బదిలీ ఈవెంట్ను పోలి ఉంటుంది, అయితే మనం మొత్తానికి బదులుగా token_idని నివేదిస్తాము. శూన్య చిరునామా ఎవరికీ స్వంతం కాదు, కాబట్టి సాంప్రదాయం ప్రకారం టోకెన్ల సృష్టి మరియు విధ్వంసాన్ని నివేదించడానికి మనం దీనిని ఉపయోగిస్తాము. దీనికి ఒక మినహాయింపు కాంట్రాక్ట్ సృష్టి, ఈ సమయంలో Transferని విడుదల చేయకుండానే ఎన్ని NFTలైనా సృష్టించబడవచ్చు మరియు కేటాయించబడవచ్చు.
ERC-721 ఆమోదం అనేది ERC-20 అనుమతి మొత్తాన్ని పోలి ఉంటుంది: నిర్దిష్ట టోకెన్ను బదిలీ చేయడానికి నిర్దిష్ట చిరునామా అనుమతించబడుతుంది మరియు ఆ ఆమోదించబడిన చిరునామా సెట్ చేయబడినప్పుడు లేదా పునరుద్ఘాటించబడినప్పుడు Approval (owner, approved, token_id) విడుదల చేయబడుతుంది. కాంట్రాక్ట్లు టోకెన్ను అంగీకరించినప్పుడు ప్రతిస్పందించడానికి ఇది ఒక యంత్రాంగాన్ని ఇస్తుంది. కాంట్రాక్ట్లు ఈవెంట్లను వినలేవు, కాబట్టి మీరు టోకెన్ను వాటికి బదిలీ చేస్తే వాటికి దాని గురించి "తెలియదు". ఈ విధంగా యజమాని మొదట ఆమోదాన్ని సమర్పిస్తాడు మరియు ఆపై కాంట్రాక్ట్కు అభ్యర్థనను పంపుతాడు: "టోకెన్ Xని బదిలీ చేయడానికి నేను మీకు ఆమోదం తెలిపాను, దయచేసి ... చేయండి". ERC-721 ప్రమాణాన్ని ERC-20 ప్రమాణానికి సమానంగా చేయడానికి ఇది ఒక డిజైన్ ఎంపిక. ERC-721 టోకెన్లు ఫంజిబుల్ కానందున, టోకెన్ యాజమాన్యాన్ని చూడటం ద్వారా కాంట్రాక్ట్ నిర్దిష్ట టోకెన్ను పొందిందని కూడా గుర్తించగలదు.
చివరగా, యజమాని కోసం ఆపరేటర్ ప్రారంభించబడినప్పుడు లేదా నిలిపివేయబడినప్పుడు ApprovalForAll (owner, operator, approved) విడుదల చేయబడుతుంది. పవర్ ఆఫ్ అటార్నీ మాదిరిగానే, నిర్దిష్ట రకానికి చెందిన ఖాతా యొక్క అన్ని టోకెన్లను (నిర్దిష్ట కాంట్రాక్ట్ ద్వారా నిర్వహించబడేవి) నిర్వహించగల ఆపరేటర్ను కలిగి ఉండటం కొన్నిసార్లు ఉపయోగకరంగా ఉంటుంది. ఉదాహరణకు, నేను ఆరు నెలల పాటు దానిని సంప్రదించలేదా అని తనిఖీ చేసే కాంట్రాక్ట్కు నేను అలాంటి అధికారాన్ని ఇవ్వాలనుకోవచ్చు మరియు అలా అయితే నా ఆస్తులను నా వారసులకు పంపిణీ చేయవచ్చు (వారిలో ఒకరు దానిని అడిగితే, లావాదేవీ ద్వారా కాల్ చేయబడకుండా కాంట్రాక్ట్లు ఏమీ చేయలేవు). ERC-20లో మనం వారసత్వ కాంట్రాక్ట్కు అధిక అనుమతి మొత్తాన్ని ఇవ్వగలము, కానీ టోకెన్లు ఫంజిబుల్ కానందున ERC-721కి అది పని చేయదు. ఇది దానికి సమానమైనది. ఈవెంట్ ఆమోదం కోసమా లేదా ఆమోదం ఉపసంహరణ కోసమా అని approved విలువ మనకు చెబుతుంది.
స్థితి వేరియబుల్స్
ఈ వేరియబుల్స్ టోకెన్ల ప్రస్తుత స్థితిని కలిగి ఉంటాయి: ఏవి అందుబాటులో ఉన్నాయి మరియు వాటిని ఎవరు కలిగి ఉన్నారు. వీటిలో చాలా వరకు HashMap ఆబ్జెక్ట్లు, రెండు రకాల మధ్య ఉండే ఏకదిశాత్మక మ్యాపింగ్లు (కొత్త ట్యాబ్లో తెరవబడుతుంది).
# @dev NFT ID నుండి దానిని కలిగి ఉన్న చిరునామాకు మ్యాపింగ్.
idToOwner: HashMap[uint256, address]
# @dev NFT ID నుండి ఆమోదించబడిన చిరునామాకు మ్యాపింగ్.
idToApprovals: HashMap[uint256, address]
ఎథీరియంలో వినియోగదారు మరియు కాంట్రాక్ట్ గుర్తింపులు 160-బిట్ చిరునామాల ద్వారా సూచించబడతాయి. ఈ రెండు వేరియబుల్స్ టోకెన్ IDల నుండి వాటి యజమానులకు మరియు వాటిని బదిలీ చేయడానికి ఆమోదించబడిన వారికి మ్యాప్ చేస్తాయి (ఒక్కొక్కరికి గరిష్టంగా ఒకటి). ఎథీరియంలో, ప్రారంభించబడని డేటా ఎల్లప్పుడూ సున్నాగా ఉంటుంది, కాబట్టి యజమాని లేదా ఆమోదించబడిన బదిలీదారు లేకపోతే ఆ టోకెన్ విలువ సున్నా అవుతుంది.
# @dev యజమాని చిరునామా నుండి అతని టోకెన్ల సంఖ్యకు మ్యాపింగ్.
ownerToNFTokenCount: HashMap[address, uint256]
ఈ వేరియబుల్ ప్రతి యజమాని కోసం టోకెన్ల సంఖ్యను కలిగి ఉంటుంది. యజమానుల నుండి టోకెన్లకు ఎటువంటి మ్యాపింగ్ లేదు, కాబట్టి నిర్దిష్ట యజమాని కలిగి ఉన్న టోకెన్లను గుర్తించడానికి ఏకైక మార్గం బ్లాక్చైన్ యొక్క ఈవెంట్ చరిత్రను తిరిగి చూడటం మరియు తగిన Transfer ఈవెంట్లను చూడటం. మన వద్ద అన్ని NFTలు ఎప్పుడు ఉన్నాయో తెలుసుకోవడానికి మరియు సమయం పరంగా మరింత వెనక్కి చూడాల్సిన అవసరం లేదని తెలుసుకోవడానికి మనం ఈ వేరియబుల్ను ఉపయోగించవచ్చు.
ఈ అల్గారిథమ్ వినియోగదారు ఇంటర్ఫేస్లు మరియు బాహ్య సర్వర్లకు మాత్రమే పని చేస్తుందని గమనించండి. బ్లాక్చైన్లో నడుస్తున్న కోడ్ గత ఈవెంట్లను చదవలేదు.
# @dev యజమాని చిరునామా నుండి ఆపరేటర్ చిరునామాల మ్యాపింగ్కు మ్యాపింగ్.
ownerToOperators: HashMap[address, HashMap[address, bool]]
ఒక ఖాతా ఒకటి కంటే ఎక్కువ ఆపరేటర్లను కలిగి ఉండవచ్చు. వాటిని ట్రాక్ చేయడానికి సాధారణ HashMap సరిపోదు, ఎందుకంటే ప్రతి కీ ఒకే విలువకు దారి తీస్తుంది. బదులుగా, మీరు HashMap[address, bool]ని విలువగా ఉపయోగించవచ్చు. అప్రమేయంగా ప్రతి చిరునామాకు విలువ Falseగా ఉంటుంది, అంటే అది ఆపరేటర్ కాదు. మీరు అవసరమైన విధంగా విలువలను Trueకి సెట్ చేయవచ్చు.
# @dev టోకెన్ను ముద్రించగల ముద్రించేవారి చిరునామా
minter: address
కొత్త టోకెన్లను ఏదో ఒక విధంగా సృష్టించాలి. ఈ కాంట్రాక్ట్లో అలా చేయడానికి అనుమతించబడిన ఒకే ఒక ఎంటిటీ ఉంది, అదే minter. ఉదాహరణకు, ఒక గేమ్కు ఇది సరిపోవచ్చు. ఇతర ప్రయోజనాల కోసం, మరింత సంక్లిష్టమైన వ్యాపార లాజిక్ను సృష్టించడం అవసరం కావచ్చు.
# @dev మద్దతు ఉన్న ERC165 ఇంటర్ఫేస్ idల స్టాటిక్ జాబితా
SUPPORTED_INTERFACES: constant(bytes4[2]) = [
# ERC165 యొక్క ERC165 ఇంటర్ఫేస్ ID
0x01ffc9a7,
# ERC721 యొక్క ERC165 ఇంటర్ఫేస్ ID
0x80ac58cd,
]
అప్లికేషన్లు దానితో ఎలా కమ్యూనికేట్ చేయగలవో, అది ఏ ERCలకు అనుగుణంగా ఉందో వెల్లడించడానికి కాంట్రాక్ట్ కోసం ఒక యంత్రాంగాన్ని ERC-165 (కొత్త ట్యాబ్లో తెరవబడుతుంది) నిర్దేశిస్తుంది. SUPPORTED_INTERFACES అనేది ఈ కాంట్రాక్ట్ అనుగుణంగా ఉండే రెండు నాలుగు-బైట్ల ఇంటర్ఫేస్ IDల స్థిరమైన జాబితా: ERC-165 మరియు ERC-721.
ఫంక్షన్లు
ఇవి వాస్తవానికి ERC-721ని అమలు చేసే ఫంక్షన్లు.
కన్స్ట్రక్టర్
@deploy
def __init__():
Pythonలో వలె Vyperలో, కన్స్ట్రక్టర్ ఫంక్షన్ను __init__ అని పిలుస్తారు. ఇది @deploy డెకరేషన్తో గుర్తించబడింది, అంటే కాంట్రాక్ట్ డిప్లాయ్ చేయబడినప్పుడు ఇది ఒకసారి రన్ అవుతుంది.
"""
@dev కాంట్రాక్ట్ కన్స్ట్రక్టర్.
"""
Pythonలో మరియు Vyperలో, మీరు బహుళ-లైన్ స్ట్రింగ్ను ("""తో ప్రారంభమై ముగిసేది) పేర్కొనడం ద్వారా మరియు దానిని ఏ విధంగానూ ఉపయోగించకుండా వ్యాఖ్యను కూడా సృష్టించవచ్చు. ఈ వ్యాఖ్యలు NatSpec (కొత్త ట్యాబ్లో తెరవబడుతుంది)ని కూడా కలిగి ఉండవచ్చు.
self.minter = msg.sender
స్థితి వేరియబుల్స్ను యాక్సెస్ చేయడానికి మీరు self.<variable name>ని ఉపయోగిస్తారు (మళ్ళీ, Pythonలో వలె). కన్స్ట్రక్టర్ కాంట్రాక్ట్ను డిప్లాయ్ చేసిన ఖాతాను minterగా రికార్డ్ చేస్తుంది.
వ్యూ ఫంక్షన్లు
ఇవి బ్లాక్చైన్ స్థితిని సవరించని ఫంక్షన్లు, కాబట్టి వాటిని బాహ్యంగా కాల్ చేస్తే ఉచితంగా అమలు చేయవచ్చు. వ్యూ ఫంక్షన్లను కాంట్రాక్ట్ ద్వారా కాల్ చేస్తే, అవి ఇప్పటికీ ప్రతి నోడ్లో అమలు చేయబడాలి మరియు అందువల్ల గ్యాస్ ఖర్చవుతుంది.
@view
@external
ఫంక్షన్ నిర్వచనానికి ముందు ఎట్ గుర్తు (@)తో ప్రారంభమయ్యే ఈ కీవర్డ్లను డెకరేషన్స్ అని పిలుస్తారు. ఫంక్షన్ను ఏ పరిస్థితులలో కాల్ చేయవచ్చో అవి నిర్దేశిస్తాయి.
@viewఈ ఫంక్షన్ ఒక వ్యూ అని నిర్దేశిస్తుంది.@externalఈ నిర్దిష్ట ఫంక్షన్ను లావాదేవీలు మరియు ఇతర కాంట్రాక్ట్ల ద్వారా కాల్ చేయవచ్చని నిర్దేశిస్తుంది.
def supportsInterface(interface_id: bytes4) -> bool:
Pythonకి విరుద్ధంగా, Vyper అనేది స్టాటిక్ టైప్డ్ లాంగ్వేజ్ (కొత్త ట్యాబ్లో తెరవబడుతుంది). డేటా రకాన్ని (కొత్త ట్యాబ్లో తెరవబడుతుంది) గుర్తించకుండా మీరు వేరియబుల్ లేదా ఫంక్షన్ పారామితిని ప్రకటించలేరు. ఈ సందర్భంలో ఇన్పుట్ పారామితి bytes4, ఇది నాలుగు-బైట్ల విలువ, మరియు అవుట్పుట్ బూలియన్ విలువ.
"""
@dev ఇంటర్ఫేస్ గుర్తింపు ERC-165లో నిర్దేశించబడింది.
@param interface_id ఇంటర్ఫేస్ యొక్క Id
"""
return interface_id in SUPPORTED_INTERFACES
SUPPORTED_INTERFACES జాబితాలోని ఇంటర్ఫేస్ IDలలో interface_id ఒకటి అయితే Trueని తిరిగి ఇస్తుంది.
### వ్యూ ఫంక్షన్లు ###
ఇవి టోకెన్ల గురించిన సమాచారాన్ని వినియోగదారులకు మరియు ఇతర కాంట్రాక్ట్లకు అందుబాటులో ఉంచే వ్యూ ఫంక్షన్లు.
@view
@external
def balanceOf(_owner: address) -> uint256:
"""
@dev `_owner` కలిగి ఉన్న NFTల సంఖ్యను తిరిగి ఇస్తుంది.
`_owner` శూన్య చిరునామా అయితే త్రో చేస్తుంది. శూన్య చిరునామాకు కేటాయించబడిన NFTలు చెల్లనివిగా పరిగణించబడతాయి.
@param _owner బ్యాలెన్స్ను క్వెరీ చేయాల్సిన చిరునామా.
"""
assert _owner != empty(address)
ఈ లైన్ _owner శూన్య చిరునామా కాదని నిర్ధారిస్తుంది (కొత్త ట్యాబ్లో తెరవబడుతుంది), ఇది empty(address)గా వ్రాయబడింది. అలా అయితే, లోపం ఏర్పడుతుంది మరియు ఆపరేషన్ రివర్ట్ చేయబడుతుంది.
return self.ownerToNFTokenCount[_owner]
@view
@external
def ownerOf(_tokenId: uint256) -> address:
"""
@dev NFT యజమాని చిరునామాను తిరిగి ఇస్తుంది.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది.
@param _tokenId NFT కోసం ఐడెంటిఫైయర్.
"""
owner: address = self.idToOwner[_tokenId]
# `_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది
assert owner != empty(address)
return owner
ఎథీరియం వర్చువల్ మెషిన్ (EVM)లో విలువ నిల్వ చేయబడని ఏదైనా స్టోరేజ్ సున్నాగా ఉంటుంది. _tokenId వద్ద టోకెన్ లేకపోతే self.idToOwner[_tokenId] విలువ సున్నా అవుతుంది. ఆ సందర్భంలో ఫంక్షన్ రివర్ట్ అవుతుంది.
@view
@external
def getApproved(_tokenId: uint256) -> address:
"""
@dev ఒకే NFT కోసం ఆమోదించబడిన చిరునామాను పొందండి.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది.
@param _tokenId ఆమోదాన్ని క్వెరీ చేయాల్సిన NFT యొక్క ID.
"""
# `_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది
assert self.idToOwner[_tokenId] != empty(address)
return self.idToApprovals[_tokenId]
getApproved సున్నాను తిరిగి ఇవ్వగలదని గమనించండి. టోకెన్ చెల్లుబాటు అయ్యేది అయితే అది self.idToApprovals[_tokenId]ని తిరిగి ఇస్తుంది. ఆమోదించేవారు లేకపోతే ఆ విలువ సున్నా అవుతుంది.
@view
@external
def isApprovedForAll(_owner: address, _operator: address) -> bool:
"""
@dev `_owner` కోసం `_operator` ఆమోదించబడిన ఆపరేటర్ అవునా కాదా అని తనిఖీ చేస్తుంది.
@param _owner NFTలను కలిగి ఉన్న చిరునామా.
@param _operator యజమాని తరపున వ్యవహరించే చిరునామా.
"""
return (self.ownerToOperators[_owner])[_operator]
ఈ కాంట్రాక్ట్లోని _owner యొక్క అన్ని టోకెన్లను నిర్వహించడానికి _operator అనుమతించబడ్డాడా లేదా అని ఈ ఫంక్షన్ తనిఖీ చేస్తుంది. బహుళ ఆపరేటర్లు ఉండవచ్చు కాబట్టి, ఇది రెండు స్థాయిల HashMap.
బదిలీ హెల్పర్ ఫంక్షన్లు
ఈ ఫంక్షన్లు టోకెన్లను బదిలీ చేయడంలో లేదా నిర్వహించడంలో భాగమైన ఆపరేషన్లను అమలు చేస్తాయి.
### బదిలీ ఫంక్షన్ హెల్పర్లు ###
@view
@internal
ఈ డెకరేషన్, @internal, అంటే ఫంక్షన్ ఒకే కాంట్రాక్ట్లోని ఇతర ఫంక్షన్ల నుండి మాత్రమే యాక్సెస్ చేయబడుతుంది. సాంప్రదాయం ప్రకారం, ఈ ఫంక్షన్ పేర్లు కూడా అండర్స్కోర్ (_)తో ప్రారంభమవుతాయి.
def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool:
"""
@dev ఇచ్చిన ఖర్చు చేసేవారు ఇచ్చిన టోకెన్ IDని బదిలీ చేయగలరా లేదా అని తిరిగి ఇస్తుంది
@param spender క్వెరీ చేయాల్సిన ఖర్చు చేసేవారి చిరునామా
@param tokenId బదిలీ చేయాల్సిన టోకెన్ యొక్క uint256 ID
@return bool msg.sender ఇచ్చిన టోకెన్ ID కోసం ఆమోదించబడ్డారా,
యజమాని యొక్క ఆపరేటర్ అవునా, లేదా టోకెన్ యజమాని అవునా అనేది
"""
owner: address = self.idToOwner[_tokenId]
spenderIsOwner: bool = owner == _spender
spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId]
spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender]
return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll
చిరునామా టోకెన్ను బదిలీ చేయడానికి అనుమతించబడే మూడు మార్గాలు ఉన్నాయి:
- చిరునామా టోకెన్ యజమాని
- ఆ టోకెన్ను ఖర్చు చేయడానికి చిరునామా ఆమోదించబడింది
- చిరునామా టోకెన్ యజమాని కోసం ఆపరేటర్
పై ఫంక్షన్ స్థితిని మార్చదు కాబట్టి ఇది ఒక వ్యూ కావచ్చు. నిర్వహణ ఖర్చులను తగ్గించడానికి, వ్యూ కాగల ఏదైనా ఫంక్షన్ వ్యూ కావాలి.
@internal
def _addTokenTo(_to: address, _tokenId: uint256):
"""
@dev ఇచ్చిన చిరునామాకు NFTని జోడించండి
`_tokenId` ఎవరికైనా స్వంతమైతే త్రో చేస్తుంది.
"""
# `_tokenId` ఎవరికైనా స్వంతమైతే త్రో చేస్తుంది
assert self.idToOwner[_tokenId] == empty(address)
# యజమానిని మార్చండి
self.idToOwner[_tokenId] = _to
# కౌంట్ ట్రాకింగ్ను మార్చండి
self.ownerToNFTokenCount[_to] += 1
@internal
def _removeTokenFrom(_from: address, _tokenId: uint256):
"""
@dev ఇచ్చిన చిరునామా నుండి NFTని తీసివేయండి
`_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది.
"""
# `_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది
assert self.idToOwner[_tokenId] == _from
# యజమానిని మార్చండి
self.idToOwner[_tokenId] = empty(address)
# కౌంట్ ట్రాకింగ్ను మార్చండి
self.ownerToNFTokenCount[_from] -= 1
బదిలీలో సమస్య ఉన్నప్పుడు మనం కాల్ను రివర్ట్ చేస్తాము.
@internal
def _clearApproval(_owner: address, _tokenId: uint256):
"""
@dev ఇచ్చిన చిరునామా ఆమోదాన్ని క్లియర్ చేయండి
`_owner` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది.
"""
# `_owner` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది
assert self.idToOwner[_tokenId] == _owner
if self.idToApprovals[_tokenId] != empty(address):
# ఆమోదాలను రీసెట్ చేయండి
self.idToApprovals[_tokenId] = empty(address)
అవసరమైతే మాత్రమే విలువను మార్చండి. స్థితి వేరియబుల్స్ స్టోరేజ్లో ఉంటాయి. స్టోరేజ్కు వ్రాయడం అనేది EVM (ఎథీరియం వర్చువల్ మెషిన్) చేసే అత్యంత ఖరీదైన ఆపరేషన్లలో ఒకటి (గ్యాస్ పరంగా). కాబట్టి, దానిని తగ్గించడం మంచి ఆలోచన, ఇప్పటికే ఉన్న విలువను వ్రాయడం కూడా అధిక ఖర్చుతో కూడుకున్నది.
@internal
def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address):
"""
@dev NFT బదిలీని అమలు చేయండి.
`msg.sender` ప్రస్తుత యజమాని, అధీకృత ఆపరేటర్ లేదా ఈ NFT కోసం ఆమోదించబడిన
చిరునామా కాకపోతే త్రో చేస్తుంది. (గమనిక: ప్రైవేట్ ఫంక్షన్లో `msg.sender` అనుమతించబడదు కాబట్టి `_sender`ని పాస్ చేయండి.)
`_to` శూన్య చిరునామా అయితే త్రో చేస్తుంది.
`_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది.
"""
టోకెన్లను బదిలీ చేయడానికి రెండు మార్గాలు (సాధారణ మరియు సురక్షితమైనవి) ఉన్నందున మనకు ఈ అంతర్గత ఫంక్షన్ ఉంది, కానీ ఆడిటింగ్ను సులభతరం చేయడానికి మనం దానిని చేసే కోడ్లో ఒకే స్థానం మాత్రమే ఉండాలని కోరుకుంటున్నాము.
# అవసరాలను తనిఖీ చేయండి
assert self._isApprovedOrOwner(_sender, _tokenId)
# `_to` శూన్య చిరునామా అయితే త్రో చేస్తుంది
assert _to != empty(address)
# ఆమోదాన్ని క్లియర్ చేయండి. `_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది
self._clearApproval(_from, _tokenId)
# NFTని తీసివేయండి. `_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది
self._removeTokenFrom(_from, _tokenId)
# NFTని జోడించండి
self._addTokenTo(_to, _tokenId)
# బదిలీని లాగ్ చేయండి
log IERC721.Transfer(sender=_from, receiver=_to, token_id=_tokenId)
Vyperలో ఈవెంట్ను విడుదల చేయడానికి మీరు log స్టేట్మెంట్ను ఉపయోగిస్తారు (మరిన్ని వివరాల కోసం ఇక్కడ చూడండి (కొత్త ట్యాబ్లో తెరవబడుతుంది)). ఈవెంట్లు దిగుమతి చేసుకున్న ఇంటర్ఫేస్కు చెందినవి కాబట్టి, మనం వాటిని IERC721.Transferగా సూచిస్తాము మరియు వాటి ఫీల్డ్లను కీవర్డ్ ద్వారా పాస్ చేస్తాము.
బదిలీ ఫంక్షన్లు
### బదిలీ ఫంక్షన్లు ###
@external
@payable
def transferFrom(_from: address, _to: address, _tokenId: uint256):
"""
@dev `msg.sender` ప్రస్తుత యజమాని, అధీకృత ఆపరేటర్ లేదా ఈ NFT కోసం ఆమోదించబడిన
చిరునామా కాకపోతే త్రో చేస్తుంది.
`_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది.
`_to` శూన్య చిరునామా అయితే త్రో చేస్తుంది.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది.
@notice `_to` NFTలను స్వీకరించగలదని నిర్ధారించుకోవాల్సిన బాధ్యత కాలర్పై ఉంటుంది, లేకపోతే
అవి శాశ్వతంగా కోల్పోయే అవకాశం ఉంది.
@param _from NFT యొక్క ప్రస్తుత యజమాని.
@param _to కొత్త యజమాని.
@param _tokenId బదిలీ చేయాల్సిన NFT.
"""
self._transferFrom(_from, _to, _tokenId, msg.sender)
ఈ ఫంక్షన్ మిమ్మల్ని ఏకపక్ష చిరునామాకు బదిలీ చేయడానికి అనుమతిస్తుంది. ఆ చిరునామా ఒక వినియోగదారు లేదా టోకెన్లను ఎలా బదిలీ చేయాలో తెలిసిన కాంట్రాక్ట్ అయితే తప్ప, మీరు బదిలీ చేసే ఏ టోకెన్ అయినా ఆ చిరునామాలో చిక్కుకుపోతుంది మరియు నిరుపయోగంగా మారుతుంది.
IERC721 ఇంటర్ఫేస్ transferFrom, safeTransferFrom మరియు approveలను చెల్లించదగినవిగా (payable) ప్రకటిస్తుంది కాబట్టి ఇక్కడ @payable డెకరేషన్ ఉంది, కాబట్టి ఇంటర్ఫేస్ను అమలు చేసే కాంట్రాక్ట్ ఆ సంతకాలతో సరిపోలాలి.
@external
@payable
def safeTransferFrom(
_from: address,
_to: address,
_tokenId: uint256,
_data: Bytes[1024]=b""
):
"""
@dev ఒక NFT యాజమాన్యాన్ని ఒక చిరునామా నుండి మరొక చిరునామాకు బదిలీ చేస్తుంది.
`msg.sender` ప్రస్తుత యజమాని, అధీకృత ఆపరేటర్ లేదా ఈ NFT కోసం
ఆమోదించబడిన చిరునామా కాకపోతే త్రో చేస్తుంది.
`_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది.
`_to` శూన్య చిరునామా అయితే త్రో చేస్తుంది.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది.
`_to` స్మార్ట్ కాంట్రాక్ట్ అయితే, అది `_to`లో `onERC721Received`ని కాల్ చేస్తుంది మరియు
తిరిగి ఇచ్చే విలువ `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` కాకపోతే త్రో చేస్తుంది.
@param _from NFT యొక్క ప్రస్తుత యజమాని.
@param _to కొత్త యజమాని.
@param _tokenId బదిలీ చేయాల్సిన NFT.
@param _data నిర్దిష్ట ఫార్మాట్ లేని అదనపు డేటా, `_to`కి కాల్లో పంపబడుతుంది.
"""
self._transferFrom(_from, _to, _tokenId, msg.sender)
ముందుగా బదిలీ చేయడం సబబే, ఎందుకంటే ఏదైనా సమస్య ఉంటే మనం ఎలాగైనా రివర్ట్ చేస్తాము, కాబట్టి కాల్లో చేసినదంతా రద్దు చేయబడుతుంది.
if _to.is_contract: # `_to` కాంట్రాక్ట్ చిరునామా అవునో కాదో తనిఖీ చేయండి
ముందుగా చిరునామా కాంట్రాక్ట్ అవునో కాదో తనిఖీ చేయండి (దానికి కోడ్ ఉంటే). కాకపోతే, అది వినియోగదారు చిరునామా అని మరియు వినియోగదారు టోకెన్ను ఉపయోగించగలరని లేదా బదిలీ చేయగలరని భావించండి. కానీ ఇది మిమ్మల్ని తప్పుడు భద్రతా భావంలోకి నెట్టనివ్వకండి. ప్రైవేట్ కీ ఎవరికీ తెలియని చిరునామాకు మీరు వాటిని బదిలీ చేస్తే, safeTransferFromతో కూడా మీరు టోకెన్లను కోల్పోవచ్చు.
returnValue: bytes4 = extcall ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)
లక్ష్య కాంట్రాక్ట్ ERC-721 టోకెన్లను స్వీకరించగలదో లేదో చూడటానికి దానికి కాల్ చేయండి. Vyper 0.4 ఇతర కాంట్రాక్ట్లకు కాల్లను గుర్తించవలసి ఉంటుంది, కాబట్టి కాల్కు ముందు extcall అని ఉంటుంది.
# బదిలీ గమ్యస్థానం 'onERC721Received'ని అమలు చేయని కాంట్రాక్ట్ అయితే త్రో చేస్తుంది
assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes4)
గమ్యస్థానం కాంట్రాక్ట్ అయితే, కానీ ERC-721 టోకెన్లను అంగీకరించనిది (లేదా ఈ నిర్దిష్ట బదిలీని అంగీకరించకూడదని నిర్ణయించుకున్నది) అయితే, రివర్ట్ చేయండి.
@external
@payable
def approve(_approved: address, _tokenId: uint256):
"""
@dev NFT కోసం ఆమోదించబడిన చిరునామాను సెట్ చేయండి లేదా పునరుద్ఘాటించండి. శూన్య చిరునామా ఆమోదించబడిన చిరునామా లేదని సూచిస్తుంది.
`msg.sender` ప్రస్తుత NFT యజమాని లేదా ప్రస్తుత యజమాని యొక్క అధీకృత ఆపరేటర్ కాకపోతే త్రో చేస్తుంది.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది. (గమనిక: ఇది EIPలో వ్రాయబడలేదు)
`_approved` ప్రస్తుత యజమాని అయితే త్రో చేస్తుంది. (గమనిక: ఇది EIPలో వ్రాయబడలేదు)
@param _approved ఇచ్చిన NFT ID కోసం ఆమోదించబడాల్సిన చిరునామా.
@param _tokenId ఆమోదించబడాల్సిన టోకెన్ యొక్క ID.
"""
owner: address = self.idToOwner[_tokenId]
# `_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది
assert owner != empty(address)
# `_approved` ప్రస్తుత యజమాని అయితే త్రో చేస్తుంది
assert _approved != owner
సాంప్రదాయం ప్రకారం, మీకు ఆమోదించేవారు ఉండకూడదనుకుంటే, మీరు మిమ్మల్ని కాకుండా శూన్య చిరునామాను నియమిస్తారు.
# అవసరాలను తనిఖీ చేయండి
senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender
senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender]
assert (senderIsOwner or senderIsApprovedForAll)
ఆమోదాన్ని సెట్ చేయడానికి మీరు యజమాని కావచ్చు లేదా యజమాని ద్వారా అధికారం పొందిన ఆపరేటర్ కావచ్చు.
# ఆమోదాన్ని సెట్ చేయండి
self.idToApprovals[_tokenId] = _approved
log IERC721.Approval(owner=owner, approved=_approved, token_id=_tokenId)
@external
def setApprovalForAll(_operator: address, _approved: bool):
"""
@dev `msg.sender` యొక్క అన్ని ఆస్తులను నిర్వహించడానికి మూడవ పక్షానికి ("ఆపరేటర్") ఆమోదాన్ని
ప్రారంభిస్తుంది లేదా నిలిపివేస్తుంది. ఇది ApprovalForAll ఈవెంట్ను కూడా విడుదల చేస్తుంది.
`_operator` `msg.sender` అయితే త్రో చేస్తుంది. (గమనిక: ఇది EIPలో వ్రాయబడలేదు)
@notice ఆ సమయంలో పంపినవారికి ఎలాంటి టోకెన్లు లేకపోయినా ఇది పని చేస్తుంది.
@param _operator అధీకృత ఆపరేటర్ల సెట్కు జోడించాల్సిన చిరునామా.
@param _approved ఆపరేటర్లు ఆమోదించబడితే True, ఆమోదాన్ని ఉపసంహరించుకోవడానికి false.
"""
# `_operator` `msg.sender` అయితే త్రో చేస్తుంది
assert _operator != msg.sender
self.ownerToOperators[msg.sender][_operator] = _approved
log IERC721.ApprovalForAll(owner=msg.sender, operator=_operator, approved=_approved)
కొత్త టోకెన్లను ముద్రించండి మరియు ఉన్నవాటిని నాశనం చేయండి
కాంట్రాక్ట్ను సృష్టించిన ఖాతా minter, ఇది కొత్త NFTలను ముద్రించడానికి అధికారం ఉన్న సూపర్ యూజర్.
అయితే, ఇది కూడా ఇప్పటికే ఉన్న టోకెన్లను దహనం చేయడానికి అనుమతించబడదు. యజమాని లేదా యజమాని ద్వారా
అధికారం పొందిన ఎంటిటీ మాత్రమే అలా చేయగలదు.
### ముద్రించు & దహనం చేయు ఫంక్షన్లు ###
@external
def mint(_to: address, _tokenId: uint256) -> bool:
ఈ ఫంక్షన్ ఎల్లప్పుడూ Trueని తిరిగి ఇస్తుంది, ఎందుకంటే ఆపరేషన్ విఫలమైతే అది రివర్ట్ చేయబడుతుంది.
"""
@dev టోకెన్లను ముద్రించడానికి ఫంక్షన్
`msg.sender` మింటర్ కాకపోతే త్రో చేస్తుంది.
`_to` శూన్య చిరునామా అయితే త్రో చేస్తుంది.
`_tokenId` ఎవరికైనా స్వంతమైతే త్రో చేస్తుంది.
@param _to ముద్రించిన టోకెన్లను స్వీకరించే చిరునామా.
@param _tokenId ముద్రించాల్సిన టోకెన్ id.
@return ఆపరేషన్ విజయవంతమైందో లేదో సూచించే బూలియన్.
"""
# `msg.sender` మింటర్ కాకపోతే త్రో చేస్తుంది
assert msg.sender == self.minter
ముద్రించేవారు (ERC-721 కాంట్రాక్ట్ను సృష్టించిన ఖాతా) మాత్రమే కొత్త టోకెన్లను ముద్రించగలరు. భవిష్యత్తులో మనం ముద్రించేవారి గుర్తింపును మార్చాలనుకుంటే ఇది సమస్య కావచ్చు. ప్రొడక్షన్ కాంట్రాక్ట్లో ముద్రించేవారు ముద్రణ అధికారాలను వేరొకరికి బదిలీ చేయడానికి అనుమతించే ఫంక్షన్ను మీరు బహుశా కోరుకుంటారు.
# `_to` శూన్య చిరునామా అయితే త్రో చేస్తుంది
assert _to != ZERO_ADDRESS
# NFTని జోడించండి. `_tokenId` ఎవరికైనా స్వంతమైతే త్రో చేస్తుంది
self._addTokenTo(_to, _tokenId)
log Transfer(ZERO_ADDRESS, _to, _tokenId)
return True
సాంప్రదాయం ప్రకారం, కొత్త టోకెన్లను ముద్రించడం అనేది శూన్య చిరునామా నుండి బదిలీగా పరిగణించబడుతుంది.
@external
def burn(_tokenId: uint256):
"""
@dev నిర్దిష్ట ERC-721 టోకెన్ను దహనం చేస్తుంది.
`msg.sender` ప్రస్తుత యజమాని, అధీకృత ఆపరేటర్ లేదా ఈ NFT కోసం ఆమోదించబడిన
చిరునామా కాకపోతే త్రో చేస్తుంది.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది.
@param _tokenId దహనం చేయాల్సిన ERC-721 టోకెన్ యొక్క uint256 id.
"""
# అవసరాలను తనిఖీ చేయండి
assert self._isApprovedOrOwner(msg.sender, _tokenId)
owner: address = self.idToOwner[_tokenId]
# `_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది
assert owner != ZERO_ADDRESS
self._clearApproval(owner, _tokenId)
self._removeTokenFrom(owner, _tokenId)
log Transfer(owner, ZERO_ADDRESS, _tokenId)
టోకెన్ను బదిలీ చేయడానికి అనుమతించబడిన ఎవరైనా దానిని దహనం చేయడానికి అనుమతించబడతారు. దహనం చేయడం అనేది శూన్య చిరునామాకు బదిలీ చేయడానికి సమానంగా కనిపించినప్పటికీ, శూన్య చిరునామా వాస్తవానికి టోకెన్ను స్వీకరించదు. టోకెన్ కోసం ఉపయోగించిన మొత్తం స్టోరేజ్ను ఖాళీ చేయడానికి ఇది మనల్ని అనుమతిస్తుంది, ఇది లావాదేవీ యొక్క గ్యాస్ ఖర్చును తగ్గించగలదు.
ఈ కాంట్రాక్ట్ను ఉపయోగించడం
Solidityకి విరుద్ధంగా, Vyperకి వారసత్వం (inheritance) లేదు. కోడ్ను స్పష్టంగా చేయడానికి మరియు తద్వారా సురక్షితంగా ఉంచడాన్ని సులభతరం చేయడానికి ఇది ఉద్దేశపూర్వక డిజైన్ ఎంపిక. కాబట్టి మీ స్వంత Vyper ERC-721 కాంట్రాక్ట్ను సృష్టించడానికి మీరు ఈ కాంట్రాక్ట్ను (కొత్త ట్యాబ్లో తెరవబడుతుంది) తీసుకుని, మీకు కావలసిన వ్యాపార లాజిక్ను అమలు చేయడానికి దానిని సవరించండి.
ముగింపు
సమీక్ష కోసం, ఈ కాంట్రాక్ట్లోని కొన్ని ముఖ్యమైన ఆలోచనలు ఇక్కడ ఉన్నాయి:
- సురక్షితమైన బదిలీతో ERC-721 టోకెన్లను స్వీకరించడానికి, కాంట్రాక్ట్లు
ERC721Receiverఇంటర్ఫేస్ను అమలు చేయాలి. - మీరు సురక్షితమైన బదిలీని ఉపయోగించినప్పటికీ, ప్రైవేట్ కీ తెలియని చిరునామాకు మీరు వాటిని పంపితే టోకెన్లు ఇప్పటికీ చిక్కుకుపోవచ్చు.
- ఆపరేషన్లో సమస్య ఉన్నప్పుడు కేవలం వైఫల్య విలువను తిరిగి ఇవ్వడం కంటే కాల్ను
revertచేయడం మంచి ఆలోచన. - ERC-721 టోకెన్లు యజమానిని కలిగి ఉన్నప్పుడు ఉనికిలో ఉంటాయి.
- NFTని బదిలీ చేయడానికి అధికారం పొందడానికి మూడు మార్గాలు ఉన్నాయి. మీరు యజమాని కావచ్చు, నిర్దిష్ట టోకెన్ కోసం ఆమోదించబడవచ్చు లేదా యజమాని యొక్క అన్ని టోకెన్లకు ఆపరేటర్ కావచ్చు.
- గత ఈవెంట్లు బ్లాక్చైన్ వెలుపల మాత్రమే కనిపిస్తాయి. బ్లాక్చైన్ లోపల నడుస్తున్న కోడ్ వాటిని చూడలేదు.
ఇప్పుడు వెళ్లి సురక్షితమైన Vyper కాంట్రాక్ట్లను అమలు చేయండి.
నా మరిన్ని పనుల కోసం ఇక్కడ చూడండి (కొత్త ట్యాబ్లో తెరవబడుతుంది).