Vyper ERC-721 కాంట్రాక్ట్ వాక్త్రూ
పరిచయం
నాన్-ఫంజిబుల్ టోకెన్ల (NFT) యాజమాన్యాన్ని కలిగి ఉండటానికి ERC-721 ప్రమాణం ఉపయోగించబడుతుంది. ERC-20 టోకెన్లు ఒక వస్తువు వలె ప్రవర్తిస్తాయి, ఎందుకంటే వ్యక్తిగత టోకెన్ల మధ్య ఎటువంటి తేడా ఉండదు. దానికి విరుద్ధంగా, ERC-721 టోకెన్లు ఒకేలా ఉండే కానీ ఖచ్చితంగా ఒకేలా ఉండని ఆస్తుల కోసం రూపొందించబడ్డాయి, ఉదాహరణకు విభిన్న పిల్లి కార్టూన్లు (opens in a new tab) లేదా వివిధ రియల్ ఎస్టేట్ ఆస్తుల టైటిల్స్.
ఈ వ్యాసంలో మనం Ryuya Nakamura యొక్క ERC-721 కాంట్రాక్ట్ (opens in a new tab)ను విశ్లేషిస్తాము. ఈ కాంట్రాక్ట్ Vyper (opens in a new tab)లో వ్రాయబడింది, ఇది Solidityలో కంటే అసురక్షిత కోడ్ను వ్రాయడం కష్టతరం చేయడానికి రూపొందించబడిన Python లాంటి కాంట్రాక్ట్ భాష.
కాంట్రాక్ట్
# @dev ERC-721 నాన్-ఫంజిబుల్ టోకెన్ ప్రమాణం యొక్క అమలు.
# @author Ryuya Nakamura (@nrryuya)
# దీని నుండి సవరించబడింది: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy
Pythonలో వలె Vyperలో వ్యాఖ్యలు హాష్ (#)తో ప్రారంభమవుతాయి మరియు లైన్ చివరి వరకు కొనసాగుతాయి. @<keyword>ని కలిగి ఉన్న వ్యాఖ్యలు మానవులు చదవగలిగే డాక్యుమెంటేషన్ను రూపొందించడానికి NatSpec (opens in a new tab) ద్వారా ఉపయోగించబడతాయి.
from vyper.interfaces import ERC721
implements: ERC721
ERC-721 ఇంటర్ఫేస్ Vyper భాషలో నిర్మించబడింది. మీరు కోడ్ నిర్వచనాన్ని ఇక్కడ చూడవచ్చు (opens in a new tab). ఇంటర్ఫేస్ నిర్వచనం Vyperకి బదులుగా Pythonలో వ్రాయబడింది, ఎందుకంటే ఇంటర్ఫేస్లు బ్లాక్చైన్లో మాత్రమే కాకుండా, బాహ్య క్లయింట్ నుండి బ్లాక్చైన్కు లావాదేవీని పంపుతున్నప్పుడు కూడా ఉపయోగించబడతాయి, ఇది Pythonలో వ్రాయబడి ఉండవచ్చు.
మొదటి లైన్ ఇంటర్ఫేస్ను దిగుమతి చేస్తుంది మరియు రెండవది మనం దానిని ఇక్కడ అమలు చేస్తున్నామని నిర్దేశిస్తుంది.
ERC721Receiver ఇంటర్ఫేస్
# safeTransferFrom() ద్వారా పిలవబడే కాంట్రాక్ట్ కోసం ఇంటర్ఫేస్
interface ERC721Receiver:
def onERC721Received(
ERC-721 రెండు రకాల బదిలీలకు మద్దతు ఇస్తుంది:
transferFrom, ఇది పంపినవారిని ఏదైనా గమ్యస్థాన చిరునామాను పేర్కొనడానికి అనుమతిస్తుంది మరియు బదిలీ బాధ్యతను పంపినవారిపై ఉంచుతుంది. దీని అర్థం మీరు చెల్లని చిరునామాకు బదిలీ చేయవచ్చు, ఆ సందర్భంలో NFT శాశ్వతంగా కోల్పోబడుతుంది.safeTransferFrom, ఇది గమ్యస్థాన చిరునామా కాంట్రాక్ట్ అవునా కాదా అని తనిఖీ చేస్తుంది. అలా అయితే, ERC-721 కాంట్రాక్ట్ స్వీకరించే కాంట్రాక్ట్ను అది NFTని స్వీకరించాలనుకుంటుందా అని అడుగుతుంది.
safeTransferFrom అభ్యర్థనలకు సమాధానం ఇవ్వడానికి స్వీకరించే కాంట్రాక్ట్ ERC721Receiverని అమలు చేయాలి.
_operator: address,
_from: address,
_from చిరునామా టోకెన్ యొక్క ప్రస్తుత యజమాని. _operator చిరునామా బదిలీని అభ్యర్థించినది (అనుమతి మొత్తాల కారణంగా ఆ రెండూ ఒకేలా ఉండకపోవచ్చు).
_tokenId: uint256,
ERC-721 టోకెన్ IDలు 256 బిట్లు. సాధారణంగా టోకెన్ దేనిని సూచిస్తుందో దాని వివరణను హాషింగ్ చేయడం ద్వారా అవి సృష్టించబడతాయి.
_data: Bytes[1024]
అభ్యర్థన గరిష్టంగా 1024 బైట్ల వినియోగదారు డేటాను కలిగి ఉండవచ్చు.
) -> bytes32: view
కాంట్రాక్ట్ పొరపాటున బదిలీని అంగీకరించే సందర్భాలను నివారించడానికి, తిరిగి ఇచ్చే విలువ బూలియన్ కాదు, కానీ నిర్దిష్ట విలువతో కూడిన 256 బిట్లు.
ఈ ఫంక్షన్ ఒక view, అంటే ఇది బ్లాక్చైన్ యొక్క స్థితిని చదవగలదు, కానీ దానిని సవరించలేదు.
ఈవెంట్లు
బ్లాక్చైన్ వెలుపల ఉన్న వినియోగదారులకు మరియు సర్వర్లకు ఈవెంట్ల గురించి తెలియజేయడానికి ఈవెంట్లు విడుదల చేయబడతాయి. ఈవెంట్ల కంటెంట్ బ్లాక్చైన్లోని కాంట్రాక్ట్లకు అందుబాటులో ఉండదని గమనించండి.
# @dev ఏదైనా యంత్రాంగం ద్వారా ఏదైనా NFT యాజమాన్యం మారినప్పుడు వెలువడుతుంది. NFTలు ఇలా ఉన్నప్పుడు ఈ ఈవెంట్ వెలువడుతుంది
# సృష్టించబడినప్పుడు (`from` == 0) మరియు నాశనం చేయబడినప్పుడు (`to` == 0). మినహాయింపు: కాంట్రాక్ట్ సృష్టి సమయంలో, ఏవైనా
# సంఖ్యలో NFTలు సృష్టించబడవచ్చు మరియు బదిలీ (Transfer) వెలువరించకుండా కేటాయించబడవచ్చు. ఏదైనా సమయంలో
# బదిలీ జరిగినప్పుడు, ఆ NFT కోసం ఆమోదించబడిన చిరునామా (ఏదైనా ఉంటే) ఏదీ లేనట్లుగా రీసెట్ చేయబడుతుంది.
# @param _from NFT పంపినవారు (చిరునామా శూన్య చిరునామా అయితే అది టోకెన్ సృష్టిని సూచిస్తుంది).
# @param _to NFT స్వీకరించేవారు (చిరునామా శూన్య చిరునామా అయితే అది టోకెన్ నాశనాన్ని సూచిస్తుంది).
# @param _tokenId బదిలీ చేయబడిన NFT.
event Transfer:
sender: indexed(address)
receiver: indexed(address)
tokenId: indexed(uint256)
ఇది ERC-20 బదిలీ ఈవెంట్ను పోలి ఉంటుంది, అయితే మనం మొత్తానికి బదులుగా tokenIdని నివేదిస్తాము. శూన్య చిరునామా ఎవరికీ స్వంతం కాదు, కాబట్టి సాంప్రదాయం ప్రకారం టోకెన్ల సృష్టి మరియు విధ్వంసాన్ని నివేదించడానికి మనం దానిని ఉపయోగిస్తాము.
# @dev NFT కోసం ఆమోదించబడిన చిరునామా మార్చబడినప్పుడు లేదా పునరుద్ఘాటించబడినప్పుడు ఇది వెలువడుతుంది. శూన్య
# చిరునామా ఆమోదించబడిన చిరునామా ఏదీ లేదని సూచిస్తుంది. బదిలీ (Transfer) ఈవెంట్ వెలువడినప్పుడు, ఇది కూడా
# ఆ NFT కోసం ఆమోదించబడిన చిరునామా (ఏదైనా ఉంటే) ఏదీ లేనట్లుగా రీసెట్ చేయబడిందని సూచిస్తుంది.
# @param _owner NFT యజమాని.
# @param _approved మనం ఆమోదిస్తున్న చిరునామా.
# @param _tokenId మనం ఆమోదిస్తున్న NFT.
event Approval:
owner: indexed(address)
approved: indexed(address)
tokenId: indexed(uint256)
ERC-721 ఆమోదం ERC-20 అనుమతి మొత్తాన్ని పోలి ఉంటుంది. ఒక నిర్దిష్ట చిరునామా నిర్దిష్ట టోకెన్ను బదిలీ చేయడానికి అనుమతించబడుతుంది. కాంట్రాక్ట్లు టోకెన్ను అంగీకరించినప్పుడు ప్రతిస్పందించడానికి ఇది ఒక యంత్రాంగాన్ని ఇస్తుంది. కాంట్రాక్ట్లు ఈవెంట్ల కోసం వినలేవు, కాబట్టి మీరు టోకెన్ను వాటికి బదిలీ చేస్తే వాటికి దాని గురించి "తెలియదు". ఈ విధంగా యజమాని ముందుగా ఆమోదాన్ని సమర్పిస్తాడు మరియు ఆపై కాంట్రాక్ట్కు అభ్యర్థనను పంపుతాడు: "టోకెన్ Xని బదిలీ చేయడానికి నేను మీకు ఆమోదం తెలిపాను, దయచేసి ... చేయండి".
ERC-721 ప్రమాణాన్ని ERC-20 ప్రమాణానికి సమానంగా చేయడానికి ఇది ఒక డిజైన్ ఎంపిక. ఎందుకంటే ERC-721 టోకెన్లు ఫంజిబుల్ కానందున, టోకెన్ యొక్క యాజమాన్యాన్ని చూడటం ద్వారా కాంట్రాక్ట్ తనకు నిర్దిష్ట టోకెన్ వచ్చిందని కూడా గుర్తించగలదు.
# @dev యజమాని కోసం ఆపరేటర్ ప్రారంభించబడినప్పుడు లేదా నిలిపివేయబడినప్పుడు ఇది వెలువడుతుంది. ఆపరేటర్ యజమాని యొక్క
# అన్ని NFTలను నిర్వహించగలరు.
# @param _owner NFT యజమాని.
# @param _operator మనం ఆపరేటర్ హక్కులను సెట్ చేస్తున్న చిరునామా.
# @param _approved ఆపరేటర్ హక్కుల స్థితి (ఆపరేటర్ హక్కులు ఇవ్వబడితే true మరియు
# ఉపసంహరించబడితే false).
event ApprovalForAll:
owner: indexed(address)
operator: indexed(address)
approved: bool
పవర్ ఆఫ్ అటార్నీ లాగా, ఒక నిర్దిష్ట రకానికి చెందిన ఖాతా యొక్క అన్ని టోకెన్లను (నిర్దిష్ట కాంట్రాక్ట్ ద్వారా నిర్వహించబడేవి) నిర్వహించగల _ఆపరేటర్_ను కలిగి ఉండటం కొన్నిసార్లు ఉపయోగకరంగా ఉంటుంది. ఉదాహరణకు, నేను ఆరు నెలల పాటు దానిని సంప్రదించకపోతే తనిఖీ చేసే కాంట్రాక్ట్కు నేను అలాంటి అధికారాన్ని ఇవ్వాలనుకోవచ్చు, మరియు అలా అయితే నా ఆస్తులను నా వారసులకు పంపిణీ చేస్తుంది (వారిలో ఒకరు దానిని అడిగితే, లావాదేవీ ద్వారా పిలవబడకుండా కాంట్రాక్ట్లు ఏమీ చేయలేవు). ERC-20లో మనం వారసత్వ కాంట్రాక్ట్కు అధిక అనుమతి మొత్తాన్ని ఇవ్వగలము, కానీ టోకెన్లు ఫంజిబుల్ కానందున ERC-721కి అది పనిచేయదు. ఇది దానికి సమానమైనది.
approved విలువ ఈవెంట్ ఆమోదం కోసమా లేదా ఆమోదాన్ని ఉపసంహరించుకోవడం కోసమా అని మనకు చెబుతుంది.
స్థితి వేరియబుల్స్
ఈ వేరియబుల్స్ టోకెన్ల ప్రస్తుత స్థితిని కలిగి ఉంటాయి: ఏవి అందుబాటులో ఉన్నాయి మరియు వాటిని ఎవరు కలిగి ఉన్నారు. వీటిలో చాలా వరకు
HashMap ఆబ్జెక్ట్లు, రెండు రకాల మధ్య ఉండే ఏకదిశాత్మక మ్యాపింగ్లు (opens in a new tab).
# @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) చిరునామా
minter: address
కొత్త టోకెన్లను ఏదో ఒక విధంగా సృష్టించాలి. ఈ కాంట్రాక్ట్లో అలా చేయడానికి అనుమతించబడిన ఒకే ఒక ఎంటిటీ ఉంది, అదే
minter. ఉదాహరణకు, ఒక గేమ్కు ఇది సరిపోవచ్చు. ఇతర ప్రయోజనాల కోసం, మరింత సంక్లిష్టమైన వ్యాపార లాజిక్ను సృష్టించడం అవసరం కావచ్చు.
# @dev ఇంటర్ఫేస్ మద్దతు ఇస్తుందా లేదా అనే దాని గురించి ఇంటర్ఫేస్ id నుండి bool కు మ్యాపింగ్
supportedInterfaces: HashMap[bytes32, bool]
# @dev ERC-165 యొక్క ERC-165 ఇంటర్ఫేస్ ID
ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
# @dev ERC-721 యొక్క ERC-165 ఇంటర్ఫేస్ ID
ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd
అప్లికేషన్లు దానితో ఎలా కమ్యూనికేట్ చేయగలవో, అది ఏ ERCలకు అనుగుణంగా ఉంటుందో వెల్లడించడానికి కాంట్రాక్ట్ కోసం ఒక యంత్రాంగాన్ని ERC-165 (opens in a new tab) నిర్దేశిస్తుంది. ఈ సందర్భంలో, కాంట్రాక్ట్ ERC-165 మరియు ERC-721కి అనుగుణంగా ఉంటుంది.
ఫంక్షన్లు
ఇవి వాస్తవానికి ERC-721ని అమలు చేసే ఫంక్షన్లు.
కన్స్ట్రక్టర్
@external
def __init__():
Pythonలో వలె Vyperలో, కన్స్ట్రక్టర్ ఫంక్షన్ను __init__ అని పిలుస్తారు.
"""
@dev కాంట్రాక్ట్ కన్స్ట్రక్టర్.
"""
Pythonలో మరియు Vyperలో, మీరు బహుళ-లైన్ స్ట్రింగ్ను ("""తో ప్రారంభమై ముగుస్తుంది) పేర్కొనడం ద్వారా మరియు దానిని ఏ విధంగానూ ఉపయోగించకుండా వ్యాఖ్యను కూడా సృష్టించవచ్చు. ఈ వ్యాఖ్యలు
NatSpec (opens in a new tab)ని కూడా కలిగి ఉండవచ్చు.
self.supportedInterfaces[ERC165_INTERFACE_ID] = True
self.supportedInterfaces[ERC721_INTERFACE_ID] = True
self.minter = msg.sender
స్థితి వేరియబుల్స్ను యాక్సెస్ చేయడానికి మీరు self.<variable name>ని ఉపయోగిస్తారు (మళ్ళీ, Pythonలో వలె).
వ్యూ ఫంక్షన్లు
ఇవి బ్లాక్చైన్ యొక్క స్థితిని సవరించని ఫంక్షన్లు, కాబట్టి వాటిని బాహ్యంగా పిలిస్తే ఉచితంగా అమలు చేయవచ్చు. వ్యూ ఫంక్షన్లను కాంట్రాక్ట్ ద్వారా పిలిస్తే, అవి ఇప్పటికీ ప్రతి నోడ్లో అమలు చేయబడాలి మరియు అందువల్ల గ్యాస్ ఖర్చు అవుతుంది.
@view
@external
ఎట్ గుర్తు (@)తో ప్రారంభమయ్యే ఫంక్షన్ నిర్వచనానికి ముందు ఉన్న ఈ కీవర్డ్లను డెకరేషన్స్ అని పిలుస్తారు. ఫంక్షన్ను ఏ పరిస్థితులలో పిలవవచ్చో అవి నిర్దేశిస్తాయి.
- ఈ ఫంక్షన్ ఒక వ్యూ అని
@viewనిర్దేశిస్తుంది. - ఈ నిర్దిష్ట ఫంక్షన్ను లావాదేవీలు మరియు ఇతర కాంట్రాక్ట్ల ద్వారా పిలవవచ్చని
@externalనిర్దేశిస్తుంది.
def supportsInterface(_interfaceID: bytes32) -> bool:
Pythonకి విరుద్ధంగా, Vyper ఒక స్టాటిక్ టైప్డ్ లాంగ్వేజ్ (opens in a new tab).
డేటా రకాన్ని (opens in a new tab) గుర్తించకుండా మీరు వేరియబుల్ లేదా ఫంక్షన్ పారామీటర్ను ప్రకటించలేరు. ఈ సందర్భంలో ఇన్పుట్ పారామీటర్ bytes32, ఇది 256-బిట్ విలువ
(256 బిట్లు అనేది ఎథీరియం వర్చువల్ మెషీన్ యొక్క స్థానిక వర్డ్ సైజు). అవుట్పుట్ బూలియన్
విలువ. సాంప్రదాయం ప్రకారం, ఫంక్షన్ పారామీటర్ల పేర్లు అండర్స్కోర్ (_)తో ప్రారంభమవుతాయి.
"""
@dev ఇంటర్ఫేస్ గుర్తింపు ERC-165 లో పేర్కొనబడింది.
@param _interfaceID ఇంటర్ఫేస్ యొక్క Id
"""
return self.supportedInterfaces[_interfaceID]
కన్స్ట్రక్టర్ (__init__)లో సెట్ చేయబడిన self.supportedInterfaces HashMap నుండి విలువను తిరిగి ఇవ్వండి.
### వ్యూ ఫంక్షన్లు ###
ఇవి వినియోగదారులకు మరియు ఇతర కాంట్రాక్ట్లకు టోకెన్ల గురించిన సమాచారాన్ని అందుబాటులో ఉంచే వ్యూ ఫంక్షన్లు.
@view
@external
def balanceOf(_owner: address) -> uint256:
"""
@dev `_owner` కలిగి ఉన్న NFTల సంఖ్యను అందిస్తుంది.
`_owner` శూన్య చిరునామా అయితే త్రో (throw) చేస్తుంది. శూన్య చిరునామాకు కేటాయించబడిన NFTలు చెల్లనివిగా పరిగణించబడతాయి.
@param _owner బ్యాలెన్స్ను ప్రశ్నించాల్సిన చిరునామా.
"""
assert _owner != ZERO_ADDRESS
_owner సున్నా కాదని ఈ లైన్ నొక్కి చెబుతుంది (opens in a new tab). ఒకవేళ అది సున్నా అయితే, అక్కడ ఒక లోపం ఉన్నట్లు మరియు ఆపరేషన్ రివర్ట్ చేయబడుతుంది.
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 != ZERO_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] != ZERO_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 ఇచ్చిన స్పెండర్ (spender) ఇచ్చిన టోకెన్ 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] == ZERO_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] = ZERO_ADDRESS
# కౌంట్ ట్రాకింగ్ను మార్చండి
self.ownerToNFTokenCount[_from] -= 1
బదిలీలో సమస్య ఉన్నప్పుడు మనం కాల్ను రివర్ట్ చేస్తాము.
@internal
def _clearApproval(_owner: address, _tokenId: uint256):
"""
@dev ఇచ్చిన చిరునామా యొక్క ఆమోదాన్ని క్లియర్ చేయండి
`_owner` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది.
"""
# `_owner` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది
assert self.idToOwner[_tokenId] == _owner
if self.idToApprovals[_tokenId] != ZERO_ADDRESS:
# ఆమోదాలను రీసెట్ చేయండి
self.idToApprovals[_tokenId] = ZERO_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 != ZERO_ADDRESS
# ఆమోదాన్ని క్లియర్ చేయండి. `_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది
self._clearApproval(_from, _tokenId)
# NFTని తీసివేయండి. `_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది
self._removeTokenFrom(_from, _tokenId)
# NFTని జోడించండి
self._addTokenTo(_to, _tokenId)
# బదిలీని లాగ్ చేయండి
log Transfer(_from, _to, _tokenId)
Vyperలో ఈవెంట్ను విడుదల చేయడానికి మీరు log స్టేట్మెంట్ను ఉపయోగిస్తారు (మరిన్ని వివరాల కోసం ఇక్కడ చూడండి (opens in a new tab)).
బదిలీ ఫంక్షన్లు
### బదిలీ ఫంక్షన్లు ###
@external
def transferFrom(_from: address, _to: address, _tokenId: uint256):
"""
@dev `msg.sender` ప్రస్తుత యజమాని, అధీకృత ఆపరేటర్ లేదా ఈ NFT కోసం ఆమోదించబడిన
చిరునామా కాకపోతే త్రో చేస్తుంది.
`_from` ప్రస్తుత యజమాని కాకపోతే త్రో చేస్తుంది.
`_to` శూన్య చిరునామా అయితే త్రో చేస్తుంది.
`_tokenId` చెల్లుబాటు అయ్యే NFT కాకపోతే త్రో చేస్తుంది.
@notice `_to` NFTలను స్వీకరించగల సామర్థ్యం కలిగి ఉందని నిర్ధారించుకోవాల్సిన బాధ్యత కాలర్ (caller) దే, లేకపోతే
అవి శాశ్వతంగా కోల్పోవచ్చు.
@param _from NFT యొక్క ప్రస్తుత యజమాని.
@param _to కొత్త యజమాని.
@param _tokenId బదిలీ చేయాల్సిన NFT.
"""
self._transferFrom(_from, _to, _tokenId, msg.sender)
ఈ ఫంక్షన్ మిమ్మల్ని ఏకపక్ష చిరునామాకు బదిలీ చేయడానికి అనుమతిస్తుంది. చిరునామా వినియోగదారు లేదా టోకెన్లను ఎలా బదిలీ చేయాలో తెలిసిన కాంట్రాక్ట్ అయితే తప్ప, మీరు బదిలీ చేసే ఏదైనా టోకెన్ ఆ చిరునామాలో చిక్కుకుపోతుంది మరియు పనికిరాదు.
@external
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)"))` కాకపోతే త్రో చేస్తుంది.
గమనిక: bytes4 ప్యాడింగ్తో bytes32 ద్వారా సూచించబడుతుంది
@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: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data)
లక్ష్య కాంట్రాక్ట్ ERC-721 టోకెన్లను స్వీకరించగలదో లేదో చూడటానికి దానికి కాల్ చేయండి.
# బదిలీ గమ్యస్థానం 'onERC721Received' ని అమలు చేయని కాంట్రాక్ట్ అయితే త్రో చేస్తుంది
assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32)
గమ్యస్థానం కాంట్రాక్ట్ అయితే, కానీ ERC-721 టోకెన్లను అంగీకరించనిది (లేదా ఈ నిర్దిష్ట బదిలీని అంగీకరించకూడదని నిర్ణయించుకున్నది) అయితే, రివర్ట్ చేయండి.
@external
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 != ZERO_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 Approval(owner, _approved, _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 ApprovalForAll(msg.sender, _operator, _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 కాంట్రాక్ట్ను సృష్టించడానికి మీరు ఈ కాంట్రాక్ట్ను (opens in a new tab) తీసుకుని, మీకు కావలసిన వ్యాపార లాజిక్ను అమలు చేయడానికి దానిని సవరించండి.
ముగింపు
సమీక్ష కోసం, ఈ కాంట్రాక్ట్లోని కొన్ని ముఖ్యమైన ఆలోచనలు ఇక్కడ ఉన్నాయి:
- సురక్షితమైన బదిలీతో ERC-721 టోకెన్లను స్వీకరించడానికి, కాంట్రాక్ట్లు
ERC721Receiverఇంటర్ఫేస్ను అమలు చేయాలి. - మీరు సురక్షితమైన బదిలీని ఉపయోగించినప్పటికీ, ప్రైవేట్ కీ తెలియని చిరునామాకు మీరు వాటిని పంపితే టోకెన్లు ఇప్పటికీ చిక్కుకుపోవచ్చు.
- ఆపరేషన్లో సమస్య ఉన్నప్పుడు కేవలం వైఫల్య విలువను తిరిగి ఇవ్వడం కంటే కాల్ను
revertచేయడం మంచి ఆలోచన. - ERC-721 టోకెన్లు యజమానిని కలిగి ఉన్నప్పుడు ఉనికిలో ఉంటాయి.
- NFTని బదిలీ చేయడానికి అధికారం పొందడానికి మూడు మార్గాలు ఉన్నాయి. మీరు యజమాని కావచ్చు, నిర్దిష్ట టోకెన్ కోసం ఆమోదించబడవచ్చు లేదా యజమాని యొక్క అన్ని టోకెన్లకు ఆపరేటర్ కావచ్చు.
- గత ఈవెంట్లు బ్లాక్చైన్ వెలుపల మాత్రమే కనిపిస్తాయి. బ్లాక్చైన్ లోపల నడుస్తున్న కోడ్ వాటిని చూడలేదు.
ఇప్పుడు వెళ్లి సురక్షితమైన Vyper కాంట్రాక్ట్లను అమలు చేయండి.