آموزش استاندارد ERC721 در سالیدیتی

استاندارد ERC721 که با نام ERC-721 نیز شناخته می‌شود، یکی از پرکاربردترین استانداردهای شبکه اتریوم برای پیاده‌سازی توکن‌های غیرقابل تعویض (NFT) به شمار می‌رود. اگر به‌دنبال یک آموزش استاندارد ERC721 در سالیدیتی هستید، این مقاله به شما کمک می‌کند با مفاهیم پایه تا نکات پیشرفته آن آشنا شوید. این استاندارد با اختصاص یک عدد منحصربه‌فرد به هر آدرس، مالکیت آن توکن را به‌صورت مستقیم به آن آدرس نسبت می‌دهد؛ یعنی اگر یک آدرس عدد مشخصی را در اختیار داشته باشد، آن عدد به‌عنوان یک NFT شناخته می‌شود که متعلق به همان آدرس است.

با وجود منابع و آموزش‌های فراوان درباره این استاندارد، بسیاری از برنامه نویسان—حتي افراد باتجربه—همچنان درک کاملی از جزئیات فنی و به‌ویژه نکات امنیتی آن ندارند. به همین دلیل، در این آموزش تصمیم گرفتیم استاندارد ERC721 را به‌گونه‌ای مستند کنیم که تمرکز اصلی آن روی بخش‌هایی باشد که اغلب از دید توسعه‌دهندگان حرفه‌ای نیز پنهان می‌مانند.

چرا NFT ها منحصربه‌فرد هستند؟

هر NFT را با سه مقدار مشخص می‌شناسیم:

  • شناسه زنجیره (chain ID)

  • آدرس قرارداد (contract address)

  • شناسه توکن (token id)

وقتی شما یک NFT دارید، در واقع مالک عددی از نوع uint256 هستید که یک قرارداد مبتنی بر استاندارد ERC721 آن را روی یکی از زنجیره‌های EVM ذخیره کرده است.

در ادامه، توابع اصلی و مکملی را بررسی می‌کنیم که رفتار استاندارد ERC721 را تعریف می‌کنند:

  • ownerOf: مپينگ مربوط به مالکیت

  • mint: ایجاد توکن

  • transferFrom: انتقال مالکیت

  • balanceOf: شمارش تعداد توکن‌ها

  • setApprovalForAll و isApprovedForAll: واگذاری حق انتقال به آدرس دیگر

  • approve و getApproved: تاییدیه اختصاصی برای یک توکن خاص

  • safeTransferFrom و _safeMint: انتقال امن توکن

  • burn: حذف یا نابودسازی توکن

مفهوم مالکیت و تابع ownerOf در ERC721

در استاندارد ERC721، مفهوم مالکیت با یک mapping ساده پیاده‌سازی می‌شود: ownerOf(uint256 id)

در واقع، این استاندارد فقط یک ارتباط ساده بین یک شناسه عددی (uint256) و آدرس مالک ایجاد می‌کند. با وجود تمام تبلیغاتی که درباره NFTها شنیده‌اید، ساختار واقعی آن‌ها چیزی جز یک mapping ساده نیست. وقتی می‌گوییم شما مالک یک NFT هستید، منظورمان این است که در mapping مربوطه، شناسه آن NFT به آدرس شما نگاشت (mapping) شده است. و تمام.

طبق مشخصات استاندارد، هر قرارداد ERC721 باید تابعی عمومی ارائه دهد که با دریافت شناسه توکن، آدرس مالک آن را برگرداند.

برای ساده‌سازی، توسعه‌دهندگان می‌توانند به‌جای تعریف یک تابع، از یک متغیر عمومی از نوع mapping استفاده کنند. از نگاه بیرونی، هر دو گزینه رفتاری مشابه دارند.

در اینجا، متغیر ownerOf یک mapping عمومی است که با دریافت شناسه توکن، آدرس مالک آن را بازمی‌گرداند.

ساخت توکن با استفاده از تابع mint

در زبان سالیدیتی، هر mapping به‌صورت پیش‌فرض مقدار صفر را برای کلیدهایی که مقداردهی نشده‌اند نگه می‌دارد. بنابراین، تا زمانی که توکنی ساخته نشده باشد، شناسه آن به آدرس صفر (address(0)) نسبت داده می‌شود. البته این مقدار به معنای مالکیت نیست. وقتی تابع ownerOf مقدار address(0) را برمی‌گرداند، یعنی توکن مورد نظر هنوز ساخته نشده و در واقع وجود ندارد.

فرآیند mint کردن همان سازوکاری است که با استفاده از آن، برای اولین بار یک توکن ساخته می‌شود و به یک آدرس خاص تعلق می‌گیرد.

تابع mint بخشی از استاندارد ERC721 نیست. توسعه‌دهندگان باید آن را با توجه به نیازهای پروژه خود پیاده‌سازی کنند. در این استاندارد الزامی وجود ندارد که توکن‌ها به ترتیب (مثل ۰، ۱، ۲ و …) ایجاد شوند. توسعه‌دهنده می‌تواند از هر الگوریتمی برای تولید شناسه‌ها استفاده کند، مثلاً با هش کردن شماره بلاک و آدرس گیرنده.

در نمونه پیاده‌سازی زیر، هر کاربر می‌تواند یک توکن با شناسه دلخواه mint کند، به‌شرط آن‌که شناسه مورد نظر قبلاً ساخته نشده باشد:

ممکن است انتشار رویداد Transfer از آدرس صفر به آدرس گیرنده در ابتدا عجیب به نظر برسد، اما این رفتار دقیقاً مطابق با استاندارد ERC721 تعریف شده است. با این کار، قرارداد اعلام می‌کند که یک توکن جدید از حالت «عدم وجود» خارج شده و وارد چرخه مالکیت شده است.

انتقال NFT با استفاده از تابع transferFrom در استاندارد ERC721

در اکثر موارد، کاربران انتظار دارند بتوانند NFTهای خود را به آدرس دیگری منتقل کنند. برای انجام این کار، قرارداد تابع transferFrom را در اختیار قرار می‌دهد.

در مثال زیر، نسخه‌ای ساده از تابع transferFrom پیاده‌سازی شده تا انتقال مستقیم مالکیت یک توکن را امکان‌پذیر کند:

شاید در نگاه اول عجیب باشد که تابع transferFrom به‌صورت payable تعریف شده، اما این ویژگی دقیقاً در استاندارد ERC721 مشخص شده است. به نظر می‌رسد این امکان را برای اپلیکیشن‌هایی فراهم کرده‌اند که هنگام انتقال یک NFT (که قبلاً ساخته شده)، نیاز به دریافت مبلغی از نوع اتر داشته باشند. البته در عمل، بسیاری از پیاده‌سازی‌ها این قابلیت را نادیده می‌گیرند چون در واقعیت به‌ندرت مورد استفاده قرار می‌گیرد.

سؤال رایج دیگری که ممکن است ذهن شما را درگیر کند این است که چرا پارامتر from در این تابع وجود دارد، در حالی که فعلاً تنها زمانی اجازه انتقال داریم که msg.sender با آدرس مالک برابر باشد؟ پاسخ دقیق این موضوع را در بخش مربوط به مکانیزم تأییدیه‌ها (approvals) بررسی خواهیم کرد. اما فعلاً کافی است بدانید که در این پیاده‌سازی ساده، فقط مالک توکن می‌تواند آن را منتقل کند.

درک عملکرد تابع balanceOf در استاندارد ERC721

استاندارد ERC721 توسعه‌دهنده را ملزم می‌کند تا تعداد NFTهایی که هر آدرس در قرارداد مالک آن‌هاست، پیگیری کند.

برای این کار، قرارداد از یک mapping با ساختار زیر استفاده می‌کند:

با افزودن این mapping، قرارداد اکنون می‌تواند تعداد توکن‌های متعلق به هر آدرس را مشخص کند. تابع balanceOf این قابلیت را در اختیار کاربران قرار می‌دهد.

نکته مهم اینجاست که balanceOf فقط تعداد توکن‌های یک آدرس را برمی‌گرداند و اطلاعاتی درباره شناسه آن‌ها ارائه نمی‌دهد. برای مثال، اگر آدرسی مالک ۳ توکن باشد، مقدار ۳ را دریافت می‌کنیم، اما نمی‌فهمیم کدام شناسه‌ها در اختیار او هستند.

برای اطمینان از صحت این مقدار، باید در تمام توابعی که می‌توانند مالکیت توکن را تغییر دهند—یعنی mint و transferFrom—مقدار balanceOf را نیز به‌روزرسانی کنیم. (در تصوير زير هايلايت شده اند)

erc721 balanceOf

یکی از نکات بسیار مهمی که باید در نظر داشته باشید این است که مالک یک NFT در هر لحظه می‌تواند توکن‌های خود را منتقل کند. به همین دلیل، زمانی که در منطق قرارداد هوشمند به تابع balanceOf تکیه می‌کنید، باید با دقت و احتیاط عمل کنید.

تابع balanceOf() را نباید به‌عنوان یک مقدار ایستا یا تغییرناپذیر در نظر گرفت؛ چرا که این مقدار در جریان یک تراکنش می‌تواند تغییر کند. برای مثال:

  • مالک ممکن است یک NFT را از یکی از آدرس‌های خودش به آدرس دیگری که خودش کنترل می‌کند منتقل کند.

  • یا حتی همان NFT را دوباره به همان آدرس فعلی انتقال دهد.

در هر دو حالت، مقدار برگشتی تابع balanceOf() تغییر می‌کند و ممکن است منطق برنامه نویسی قرارداد را دچار خطا یا حتی آسیب‌پذیری کند.

اگر قصد دارید از balanceOf برای اعمال شرط یا محدودیت در قرارداد هوشمند استفاده کنید، باید خطرات مربوط به تغییر هم‌زمان مالکیت را در نظر بگیرید و کد را به‌گونه‌ای طراحی کنید که امکان سوءاستفاده از این ویژگی وجود نداشته باشد.

تاییدیه‌های نامحدود با توابع setApprovalForAll و isApprovedForAll در ERC721

استاندارد ERC721 این امکان را فراهم کرده است که مالک یک NFT، بدون انتقال مالکیت آن، کنترل توکن را به آدرس دیگری واگذار کند. اولین ابزار برای انجام این کار، تابع setApprovalForAll() است. همان‌طور که از نام آن پیداست، این تابع به آدرس دیگر اجازه می‌دهد که به نمایندگی از مالک، تمام NFTهای او را منتقل کند.

تابع مکمل isApprovedForAll() بررسی می‌کند که آیا آدرسی خاص، که در اینجا اپراتور نام دارد، از طرف مالک مجوز دریافت کرده است یا نه.

یک مالک می‌تواند چندین اپراتور تعریف کند. این قابلیت به بازارهای NFT این امکان را می‌دهد که یک توکن را به‌صورت هم‌زمان در چند پلتفرم برای فروش قرار دهند. زمانی که بازار مورد نظر به‌عنوان اپراتور برای مالک تایید شده باشد، می‌تواند در صورت دریافت مبلغ مناسب از خریدار، توکن را به او منتقل کند.

erc721 approveForAll

از این پس، تابع transferFrom این قابلیت را فراهم می‌کند که هم خود مالک و هم آدرسی که از طریق isApprovedForAll تأیید شده است بتوانند توکن را منتقل کنند.

تاییدیه برای یک توکن خاص با استفاده از توابع approve و getApproved در ERC721

به‌جای آن‌که یک آدرس را برای مدیریت همه NFTهای خود تأیید کنید، می‌توانید فقط برای یک شناسه خاص (id) این اجازه را صادر کنید. این روش معمولاً ایمن‌تر است و به شما کنترل دقیق‌تری می‌دهد. تاییدیه صادرشده در mapping عمومی به نام getApproved() ذخیره می‌شود.

بر خلاف تابع isApprovedForAll، در این نوع تاییدیه هیچ ارتباطی با آدرس مالک وجود ندارد و مجوز فقط به شناسه توکن خاص مربوط می‌شود.

پس از انتقال توکن، معمولاً مالک جدید نمی‌خواهد که فرد دیگری همچنان اجازه انتقال آن شناسه را داشته باشد. به همین دلیل، تابع transferFrom باید به‌گونه‌ای نوشته شود که تاییدیه مربوط به آن شناسه را پاک‌سازی کند.

یکی از محدودیت‌های تابع approve این است که تنها می‌توان یک آدرس را برای هر شناسه توکن تأیید کرد. اگر بخواهید چندین آدرس را برای یک توکن خاص مجاز کنید، حذف همه آن‌ها هنگام انتقال می‌تواند از نظر مصرف گس هزینه‌بر باشد.

همچنین توجه داشته باشید که اگر یک آدرس از طریق setApprovalForAll به‌عنوان اپراتور تأیید شده باشد، می‌تواند برای توکن‌هایی که متعلق به آدرس تحت پوشش او هستند، آدرس‌های دیگری را هم تأیید کند. این رفتار بخشی از طراحی استاندارد است و عملکرد تابع setApprovalForAll() تغییری نمی‌کند.

erc721 approve

پس از انتقال مالکیت، تاییدیه‌های قبلی پاک می‌شوند، زیرا معمولاً مالک جدید نمی‌خواهد آدرس قبلی همچنان اجازه دسترسی به آن شناسه را داشته باشد.

در این مرحله، تقریباً تمام توابع اصلی مورد نیاز استاندارد ERC721 را پیاده‌سازی کرده‌ایم. البته برخی از توابع باقی‌مانده به مستندات و توضیحات گسترده‌تری نیاز دارند.

شناسایی NFTهای تحت مالکیت يک آدرس بدون استفاده از افزونه Enumerable

چطور می‌توان فهرستی از شناسه‌های NFT که یک آدرس در اختیار دارد تهیه کرد؟

با استفاده از توابعی که تاکنون بررسی کرده‌ایم، متأسفانه هیچ روش کارآمدی برای شناسایی توکن‌های متعلق به یک آدرس خاص وجود ندارد.

تابع balanceOf فقط تعداد توکن‌های یک آدرس را برمی‌گرداند، و تابع ownerOf فقط نشان می‌دهد که مالک یک شناسه خاص چه کسی است. اگر بخواهیم بفهمیم کدام شناسه‌ها به یک آدرس تعلق دارند، باید روی تمام شناسه‌ها حلقه بزنیم و برای هرکدام ownerOf را صدا بزنیم. این روش بسیار پرهزینه و غیربهینه است.

در واقع، تا زمانی که از افزونه Enumerable استفاده نکنیم، هیچ راه بهینه‌ای برای شناسایی NFTهای متعلق به یک آدرس به‌صورت کاملاً درون‌زنجیره‌ای (on-chain) در اختیار نداریم.

اما تا زمانی که از این افزونه استفاده نکرده‌ایم، چطور باید چنین اطلاعاتی را در اختیار قرارداد بگذاریم؟

اگر یک قرارداد لازم داشته باشد بررسی کند که آدرس 0xc0ffee... مالک شناسه‌های ۵، ۷ و ۲۱ است، بهترین روش این است که خود کاربر این اطلاعات را اعلام کند، سپس قرارداد صحت این ادعا را تأیید کند.

به‌عبارت دیگر، باید آرایه‌ای از شناسه‌ها به قرارداد ارسال شود و قرارداد با استفاده از ownerOf بررسی کند که آن آدرس واقعاً مالک آن‌هاست.

اما خارج از زنجیره (off-chain) چگونه می‌توانیم به‌صورت کارآمد تشخیص دهیم که آدرس 0xc0ffee... مالک شناسه‌های ۵، ۷ و ۲۱ است؟

یکی از روش‌های ساده این است که روی همه شناسه‌ها حلقه بزنیم و برای هر کدام تابع ownerOf() را صدا بزنیم. اما این روش بسیار پرهزینه است و باعث می‌شود ارائه‌دهنده RPC شما، هزینه زیادی بابت این درخواست‌ها دریافت کند.

پردازش رویدادهای ERC721

برای شناسایی مالکیت NFTها، می‌توانیم به‌جای فراخوانی مستقیم ownerOf()، رویدادهای Transfer را اسکن کنیم. این رویدادها هنگام ایجاد، انتقال یا حتی سوزاندن توکن‌ها منتشر می‌شوند و اطلاعاتی از جمله آدرس فرستنده، گیرنده و شناسه توکن را ثبت می‌کنند.

در لینک زیر، نمونه کدی با استفاده از web3.js ارائه شده است که با اسکن این رویدادها، لیست NFTهای تحت مالکیت هر آدرس را استخراج می‌کند:

https://gist.github.com/RareSkills/5d60ad42cdd81b6e136605a832ba59ee

توجه داشته باشید که این کد تمام بلاک‌ها را از بلاک شماره صفر اسکن می‌کند، که رویکرد بهینه‌ای نیست. برای بهبود عملکرد، می‌توانید نقطه شروع مناسبی بر اساس زمان راه‌اندازی قرارداد خود انتخاب کنید.

انتقال امن: توابع safeTransferFrom، _safeMint و onERC721Received

توابع safeTransferFrom و _safeMint به‌گونه‌ای طراحی شده‌اند که از گیر افتادن توکن‌ها در قراردادهایی که توانایی مدیریت آن‌ها را ندارند جلوگیری کنند. اگر یک NFT به قراردادی منتقل شود که امکان فراخوانی transferFrom را نداشته باشد، آن توکن عملاً در آن قرارداد قفل می‌شود و دیگر قابل استفاده نخواهد بود.

برای پیشگیری از این وضعیت، استاندارد ERC721 تنها اجازه می‌دهد توکن به قراردادهایی منتقل شود که بتوانند آن را دریافت و در صورت لزوم منتقل کنند. یک قرارداد زمانی توانایی مدیریت NFT را دارد که تابع onERC721Received() را پیاده‌سازی کرده باشد و مقدار خاص 0x150b7a02 را برگرداند.

این مقدار همان selector تابع onERC721Received() است؛ شناسه داخلی‌ای که سالیدیتی برای تشخیص توابع از آن استفاده می‌کند.

ساختار این تابع در قالب رابط زیر تعریف شده است:

در ادامه یک نمونه ساده از پیاده‌سازی این رابط ارائه شده است:
تابع safeTransferFrom از نظر عملکرد مشابه transferFrom است؛ اما در پشت صحنه، پس از انتقال توکن، بررسی می‌کند که گیرنده یک آدرس معمولی (EOA) است یا یک قرارداد هوشمند. برای انجام این تشخیص، از روشی استفاده می‌شود که در مطلب تشخیص قرارداد هوشمند در سالیدیتی به‌طور کامل توضیح داده‌ایم.

  • اگر گیرنده یک آدرس معمولی باشد، انتقال بدون هیچ تغییری انجام می‌شود.

  • اگر گیرنده یک قرارداد باشد، تابع onERC721Received() با پارامترهای مشخص‌شده روی قرارداد گیرنده فراخوانی می‌شود.

  • اگر این فراخوانی با خطا مواجه شود یا مقدار بازگشتی برابر با 0x150b7a02 نباشد، تراکنش revert می‌شود.

چرا بررسی مقدار بازگشتی تابع (function selector) ضروری است؟

این‌که onERC721Received() بدون خطا اجرا شود، به‌تنهایی کافی نیست تا مطمئن شویم قرارداد گیرنده توانایی مدیریت درست توکن ERC721 را دارد.

فرض کنید یک NFT به قراردادی منتقل شود که فقط یک تابع fallback دارد. در این حالت، اگر مقدار بازگشتی بررسی نشود، ممکن است تراکنش بدون خطا پیش برود، ولی در واقعیت، قرارداد گیرنده هیچ مکانیزمی برای نگهداری یا انتقال مجدد NFT نداشته باشد. صرفاً داشتن یک تابع fallback به‌معنای سازگاری با استاندارد ERC721 نیست.

آرگومان‌های تابع onERC721Received

هنگام فراخوانی onERC721Received()، پارامترهای زیر به آن ارسال می‌شوند:

operator:
این مقدار برابر است با msg.sender از دید تابع safeTransfer. این آدرس می‌تواند خودِ مالک توکن باشد یا آدرسی که مجوز انتقال آن را دارد.

from:
آدرس مالک اصلی NFT پیش از انتقال است. اگر خود مالک تابع transfer را فراخوانی کرده باشد، مقدار from با operator برابر خواهد بود.

tokenId:
شناسه توکن NFT که در حال انتقال است.

data:
اگر هنگام فراخوانی safeTransferFrom داده‌ای همراه با آن ارسال شده باشد، این داده به‌صورت مستقیم به قرارداد گیرنده منتقل می‌شود. بررسی دقیق‌تر پارامتر data را در بخش بعدی انجام می‌دهیم.

ملاحظات امنیتی مربوط به onERC721Received

همواره مقدار msg.sender را بررسی کنید
در حالت پیش‌فرض، هر آدرسی می‌تواند تابع onERC721Received() را با پارامترهای دلخواه صدا بزند و قرارداد شما را فریب دهد تا فکر کند NFTای دریافت کرده که در واقع ندارد. اگر قرارداد شما از این تابع استفاده می‌کند، باید بررسی کنید که msg.sender همان قرارداد ERC721 مورد نظر شما باشد.

خطر حمله بازگشتی (reentrancy) در safeTransfer

توابع safeTransfer و _safeMint کنترل اجرای برنامه را به قرارداد خارجی منتقل می‌کنند. هنگام ارسال NFT به یک آدرس دلخواه با استفاده از safeTransfer، دقت داشته باشید که گیرنده می‌تواند هرگونه منطق دلخواه را در تابع onERC721Received() پیاده‌سازی کند. این موضوع ممکن است منجر به حمله reentrancy شود. اگر قرارداد شما به‌درستی در برابر این نوع حمله ایمن‌سازی شده باشد، جای نگرانی نیست.

احتمال شکست در safeTransfer

گیرنده مخرب می‌تواند با ایجاد خطا در داخل تابع onERC721Received() یا استفاده از یک حلقه سنگین برای مصرف تمام گس موجود، باعث شود که تراکنش برگشت داده شود. بنابراین، نباید فرض کنید که safeTransferFrom همیشه با موفقیت به پایان می‌رسد.

safeTransferFrom همراه با پارامتر data و دلیل وجود آن – کاربردهای عملی و بهینه‌سازی

در استاندارد ERC721 دو نسخه از تابع safeTransferFrom تعریف شده است:

نسخه دوم شامل یک پارامتر اضافی به نام data است. این پارامتر به ما اجازه می‌دهد هنگام انتقال، اطلاعاتی اضافی مثل دستورالعمل‌ها، مقادیر رأی‌دهی، آدرس نماینده و غیره را نیز به قرارداد گیرنده ارسال کنیم.

در ادامه مثالی ارائه می‌شود که نحوه استفاده از data در ترکیب با تابع onERC721Received() را نشان می‌دهد.

استیکینگ بهینه از نظر گس، بدون نیاز به تاییدیه (approval)

یکی از الگوهای رایج در توسعه قراردادهای هوشمند، واریز یک NFT به قرارداد با هدف استیکینگ است. البته در واقعیت، NFT “وارد” قرارداد نمی‌شود. در عوض، مالکیت توکن (یعنی مقدار ownerOf برای آن شناسه خاص) به قرارداد استیکینگ منتقل می‌شود و این قرارداد اطلاعات مربوط به مالک اصلی را در خود ذخیره می‌کند.

روش مرسومی که برای این کار به کار می‌رود، در قطعه کد زیر آمده است؛ اما این روش از نظر مصرف گس بهینه نیست، زیرا کاربر باید ابتدا توکن مورد نظر را برای قرارداد استیکینگ تأیید (approve) کند و سپس تابع deposit() را صدا بزند.

در مثال ارائه‌شده، قابلیت رأی‌دهی هنگام استیک کردن نیز اضافه شده تا نشان دهد چگونه می‌توان پارامترهای اضافی را در حین انتقال توکن منتقل کرد.

روش جایگزینی که از نظر مصرف گس به‌مراتب بهینه‌تر عمل می‌کند، این است که کاربر مستقیماً از تابع safeTransfer برای انتقال NFT به قرارداد استفاده کند. با این کار، دیگر نیازی به اجرای مرحله‌ی approve نیست و در نتیجه، یک تراکنش صرفه‌جویی می‌شود.

البته برای جلوگیری از خطاهای احتمالی، باید این فرآیند از طریق رابط کاربری (frontend) به‌درستی هدایت و مدیریت شود تا تجربه کاربری ساده و بدون ابهام باقی بماند.

در این روش، مقدار vote در قالب آرگومان data به قرارداد منتقل می‌شود و قرارداد می‌تواند هنگام دریافت NFT، این اطلاعات اضافی را رمزگشایی و پردازش کند.

بار دیگر تأکید می‌کنیم: بررسی msg.sender در تابع onERC721Received یک الزام امنیتی است

اگر در تابع onERC721Received بررسی نکنید که مقدار msg.sender با آدرس قرارداد NFT مورد انتظار برابر است، هر آدرسی می‌تواند این تابع را با پارامترهای دلخواه و حتی داده‌های مخرب فراخواني کند. این موضوع می‌تواند امنیت قرارداد شما را به‌شدت تهدید کند. به همین دلیل، بررسی دقیق msg.sender در این تابع کاملاً ضروری است.

مثالی که پیش‌تر دیدید به‌خوبی نشان می‌دهد که پارامتر data تا چه اندازه می‌تواند مفید باشد. نوع داده‌ای bytes calldata data این امکان را فراهم می‌کند که هرگونه اطلاعات دلخواه را در قالب باینری فشرده‌سازی کرده و هنگام انتقال توکن به قرارداد مقصد ارسال کنیم.

در مثال فعلی، تنها یک مقدار از نوع uint8 به‌نام voteId رمزگشایی شده است. اما می‌توانیم پارامترهای بیشتری مانند intendedDuration و delegate نیز ارسال کنیم. برای رمزگشایی آن‌ها از کدی مشابه زیر استفاده می‌شود:

مقایسه مصرف گس: safeTransferFrom و _safeMint در برابر transferFrom و _mint

اگر مطمئن هستید که گیرنده توکن یک حساب معمولی (EOA) است و نه یک قرارداد هوشمند، بهتر است از توابع transferFrom یا _mint استفاده کنید. توابع safeTransferFrom و _safeMint ابتدا بررسی می‌کنند که آیا گیرنده یک قرارداد است یا نه، و این بررسی اضافی در شرایطی که نیازی به آن نیست، تنها باعث اتلاف گس خواهد شد.

تابع burn و حذف NFT

برای حذف یک NFT، می‌توانید آن را به آدرس صفر (address(0)) منتقل کنید؛ این کار معادل با سوزاندن (burn) توکن محسوب می‌شود. البته این قابلیت به‌صورت رسمی در استاندارد ERC721 گنجانده نشده، بنابراین قراردادها الزاماً نیازی به پشتیبانی از این عملیات ندارند. اگر قصد دارید در پروژه خود امکان حذف توکن را فراهم کنید، باید آن را به‌طور جداگانه پیاده‌سازی کنید.

پیاده‌سازی‌های ERC721

کتابخانه‌ای که OpenZeppelin ارائه داده، یکی از بهترین گزینه‌ها برای توسعه‌دهندگان تازه‌کار به‌شمار می‌رود؛ به‌ویژه زمانی که با مجموعه قراردادهای قابل ارتقا (Upgradeable Contracts) استفاده شود. این کتابخانه امنیت، خوانایی و قابلیت اطمینان بالایی دارد.

در مقابل، توسعه‌دهندگان حرفه‌ای‌تر که به دنبال مصرف گس کمتر و عملکرد بالاتر هستند، می‌توانند از پیاده‌سازی Solady ERC721 استفاده کنند. این نسخه بهینه‌سازی‌های پیشرفته‌ای دارد که در پروژه‌های بزرگ و پرترافیک بسیار مؤثر است.

به این مطلب امتیاز دهید

راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.

دوره صفر تا صد آموزش بین المللی لینوکس
  • انتشار: ۲۴ اردیبهشت ۱۴۰۴

دسته بندی موضوعات

آخرین محصولات فروشگاه

مشاهده همه

نظرات

بازخوردهای خود را برای ما ارسال کنید