آموزش ساخت توکن های ERC20 در سالیدیتی

آموزش ساخت توکن های ERC20 در سالیدیتی

اکنون آماده‌ایم تا اولین توکن ERC20 خود را بسازیم!

توکن های ERC20 معمولاً یک نام (name) و یک نماد (symbol) دارند. برای مثال، توکن ApeCoin دارای نام “ApeCoin” و نماد “APE” است. از آنجا که نام و نماد توکن معمولاً تغییر نمی‌کنند، آن‌ها را در سازنده (constructor) تعریف می‌کنیم و نیازی به تابعی برای تغییرشان در آینده نخواهیم داشت. همچنین آن‌ها را به‌صورت عمومی (public) تعریف می‌کنیم تا هرکسی بتواند نام و نماد توکن را مشاهده کند:

ذخیره موجودی کاربران

گام بعدی این است که موجودی کاربران مختلف را ذخیره کنیم:

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

در حال حاضر، موجودی همه صفر است؛ بنابراین باید راهی برای ایجاد توکن (Minting) وجود داشته باشد. در این مرحله، اجازه می‌دهیم که تنها آدرس خاصی — یعنی همان کسی که قرارداد را مستقر کرده — بتواند توکن تولید کند.

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

برای اینکه بدانیم چه تعداد توکن تا الان ایجاد شده‌اند، استاندارد ERC20 نیاز به یک متغیر عمومی یا تابع به نام totalSupply دارد که تعداد کل توکن‌های موجود را برمی‌گرداند.

اگر تا به حال از یک توکن ERC20 در کیف پول خود استفاده کرده باشید، احتمالاً دیده‌اید که گاهی موجودی توکن ها به صورت اعشاری نمایش داده می‌شود. اما چگونه چنین چیزی ممکن است، در حالی که متغیرهای uint256 توانایی نگهداری اعداد اعشاری را ندارند؟

بزرگ ترین عددی که یک uint256 می‌تواند نگه دارد، چنین عدد بزرگی است:

115792089237316195423570985008687907853269984665640564039457584007913129639935

بیایید کمی عدد را کاهش دهیم تا قابل درک تر شود:

10000000000000000000000000000000000000000000000000000000000000000000000000000

اگر فرض کنیم توکن ما ۱۸ رقم اعشار دارد، آن‌وقت ۱۸ صفر سمت راست این عدد به‌عنوان بخش اعشاری در نظر گرفته می‌شوند:

10000000000000000000000000000000000000000000000000000000000.000000000000000000

در نتیجه، اگر توکن ERC20 ما دارای ۱۸ رقم اعشار باشد، می‌توانیم حداکثر این تعداد کوین کامل داشته باشیم:

10000000000000000000000000000000000000000000000000000000000

که صفرهای سمت راست آن به‌عنوان اعشار در نظر گرفته می‌شوند. این مقدار معادل ۱۰ اُکتودسیلیون (octodecillion) کوین است. و برای کسانی که با این اعداد بسیار بزرگ آشنا نیستند، این عدد معادل است با:

۱ کوادریلیون × ۱ کوادریلیون × ۱ کوادریلیون × ۱ تریلیون

۱۰ اُکتودسیلیون باید برای اکثر کاربردها کافی باشد — حتی برای کشورهایی که وارد وضعیت اَبرتورم می‌شوند.

واحدهای ارزی همچنان اعداد صحیح (integer) هستند، اما این واحدها اکنون مقادیر بسیار کوچکی از کوین را نمایش می‌دهند.

عدد ۱۸ به‌عنوان رقم اعشار، استانداردی رایج در بین توکن‌ها است، اما برخی توکن‌ها مانند USDC تنها از ۶ رقم اعشار استفاده می‌کنند.

تعداد اعشار یک توکن نباید تغییر کند؛ این فقط یک تابع ساده است که مشخص می‌کند توکن چند رقم اعشار دارد و صرفاً همان عدد را برمی‌گرداند.

اگر با دقت به توضیحات ارائه‌شده توجه کرده باشید، در این بخش با نکته‌ای ظریف روبه‌رو هستیم. نوع داده ای که برای تعداد اعشار (decimals) استفاده می‌شود، uint8 است و نه uint256. دلیل این انتخاب آن است که نوع uint8 تنها قادر است مقادیری بین ۰ تا ۲۵۵ را در خود جای دهد، در حالی که یک متغیر از نوع uint256 می‌تواند عددی با حداکثر ۷۷ رقم را نمایش دهد (در صورت تمایل، می‌توانید با شمارش صفرهای اعداد ذکرشده در مثال‌های قبلی، این موضوع را بررسی کنید).

از آنجا که برای داشتن یک واحد کامل از توکن، نمی‌توان بیش از ۷۷ رقم اعشار در نظر گرفت، استاندارد ERC20 استفاده از نوع uint8 را برای تعیین تعداد اعشار الزامی کرده است. این تصمیم کاملاً منطقی است، چرا که حتی در کاربردهای خاص نیز نیاز به بیش از ۷۷ رقم اعشار وجود ندارد و چنین حالتی عملاً غیرممکن است.

حالا بیایید تابع transfer را دوباره به قراردادمان اضافه کنیم.

شاید متوجه شده باشید که در کد تابع انتقال (transfer) یک خط جدید اضافه شده است:
اما چرا اضافه کردن این شرط اهمیت دارد؟ آدرس صفر (address(0)) متعلق به هیچ کاربری نیست. بنابراین، هر توکنی که به این آدرس ارسال شود، برای همیشه غیرقابل استفاده خواهد بود. به‌عبارت دیگر، ارسال توکن به این آدرس معادل با از بین رفتن آن است. طبق عرف در استاندارد ERC20، زمانی که توکن ها به آدرس صفر منتقل می‌شوند، در واقع به‌نوعی “سوخته” شده‌اند و باید از مقدار کل عرضه (totalSupply) نیز کسر شوند. به همین دلیل، بهتر است برای چنین عملکردی یک تابع مجزا به‌نام burn تعریف شود و از ارسال مستقیم به آدرس صفر جلوگیری گردد.

مفهوم Allowance (مقدار مجاز) در سالیدیتی

اکنون به یکی از مفاهیم مهم در استاندارد ERC20 می‌پردازیم: Allowance.

Allowance یا «مقدار مجاز» این امکان را فراهم می‌کند که یک آدرس مشخص، تا سقف تعیین‌شده‌ای از توکن های شما را خرج کند.

ممکن است این سؤال پیش بیاید که چرا باید اجازه دهید شخص دیگری توکن های شما را خرج کند؟ پاسخ کامل به این سؤال مفصل است، اما خلاصه‌اش این است که انتقال توکن های ERC20 صرفاً با اجرای یک تابع و تغییر مقدار در یک mapping انجام می‌شود. بنابراین، برخلاف دنیای واقعی، شما «دریافت فیزیکی» ندارید؛ بلکه فقط مالکیت توکن ها از طریق آدرس تغییر می‌کند.

در این میان، قراردادهای هوشمند (smart contracts) نمی‌توانند مانند انسان ها وضعیت حساب ها را بررسی کرده و از انتقال توکن به خود مطلع شوند. به همین دلیل، در طراحی قراردادهای هوشمند، یک الگوی مرسوم استفاده می‌شود:

  1. ابتدا کاربر باید با استفاده از تابع approve به قرارداد هوشمند اجازه دهد تا تعداد مشخصی از توکن هایش را برداشت کند.

  2. سپس قرارداد هوشمند با فراخوانی تابع transferFrom توکن ها را از حساب کاربر برداشت می‌کند.

این الگو یکی از اجزای حیاتی در تعامل قراردادهای هوشمند با توکن های ERC20 است و استفاده گسترده‌ای در پروژه های دیفای (DeFi)، صرافی های غیرمتمرکز و سایر برنامه های غیرمتمرکز دارد.

در ادامه، ساختار داده ای برای ذخیره مقدار مجاز (allowance) و تابع approve برای تعیین این مقدار را به قرارداد خود اضافه خواهیم کرد.

در خط کد زیر:

مقدار مجاز یا Allowance برای خرج کردن توکن ها تعیین می‌شود. در این دستور، msg.sender صاحب اصلی توکن هاست و spender آدرسی است که اجازه دریافت و خرج مقدار مشخصی از توکن های صاحب حساب را دریافت می‌کند. به عبارت دیگر، صاحب حساب (owner) به یک کاربر یا قرارداد دیگر (spender) اجازه می‌دهد تا به نیابت از او، مقداری توکن خرج کند.

اما تا اینجا فقط مقدار مجاز تعریف شده است. هنوز راهی برای استفاده از این اجازه نامه وجود ندارد. اینجا است که تابع مهم دیگری وارد عمل می‌شود: transferFrom.

تحلیل عملکرد تابع transferFrom در سالیدیتی

ابتدا باید بدانیم که ممکن است خود صاحب توکن ها (owner) تابع transferFrom را صدا بزند. در این حالت، بررسی allowance ضرورتی ندارد، چرا که فردی که مجاز به خرج کردن توکن هاست همان دارنده‌ اصلی آن‌هاست. بنابراین در این حالت مستقیماً موجودی ها (balances) را به روزرسانی می‌کنیم.

در غیر این صورت (یعنی اگر msg.sender شخصی غیر از owner باشد)، باید بررسی کنیم که آیا این شخص مجاز به خرج کردن مقدار موردنظر از حساب مالک است یا خیر. برای این کار:

  1. ابتدا بررسی می‌کنیم که مقدار allowance کافی باشد.

  2. سپس مقدار خرج شده را از allowance کم می‌کنیم تا از خرج بی‌نهایت جلوگیری شود.

  3. در نهایت، انتقال را انجام می‌دهیم.

بر اساس مشخصات رسمی EIP-20، سه تابع approve، transfer و transferFrom باید پس از اجرای موفقیت آمیز مقدار true را بازگردانند. این بازگشت مقدار نشان دهنده موفقیت عملکرد تابع برای کاربران و قراردادهای دیگر است که ممکن است به این خروجی نیاز داشته باشند. پس بیایید این را اضافه کنیم:

در این بخش، نکته مهمی در طراحی قراردادهای هوشمند مطرح می‌شود: جلوگیری از تکرار کد در توابع transfer و transferFrom. همان‌طور که اشاره شد، این دو تابع بخشی از کدشان – یعنی به روزرسانی موجودی ها – مشابه یکدیگر است. این نوع تکرار در برنامه نویسی معمولاً نشانه ای برای نیاز به بازسازی (refactoring) کد است.

راه‌حل: استخراج تابع داخلی (internal function)

برای جلوگیری از تکرار، می‌توانیم منطق انتقال موجودی را در یک تابع جداگانه قرار دهیم. اما نکته بسیار مهم این است که این تابع نباید public باشد؛ در غیر این صورت، کاربران خارجی می‌توانند با صدا زدن آن، توکن ها را جابه‌جا کرده و عملاً دزدی انجام دهند!

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

خیلی تمیزتر!

5/5 - (1 امتیاز)

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

پکیج آموزش سی شارپ | مختص ورود به بازار کار + آموزش ساخت بازی Quiz of King
  • انتشار: ۱۱ اردیبهشت ۱۴۰۴

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

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

مشاهده همه

نظرات

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