توابع Payable در سالیدیتی چیست و چرا اهمیت دارد؟
در دنیای قراردادهای هوشمند، تنها کد و منطق برنامه نویسی کافی نیست؛ گاهی لازم است این قراردادها بتوانند مستقیماً با ارز دیجیتال اصلی شبکه اتریوم یعنی اتر (Ether) تعامل داشته باشند. اینجاست که مفهوم توابع Payable در سالیدیتی وارد میدان میشود.
توابعی که با کلیدواژه payable
مشخص میشوند، قابلیت دریافت اتر را دارند و به شما امکان میدهند تا قراردادتان در لحظه اجرا، مقدار مشخصی از ارز دیجیتال دریافت کند. این ویژگی برای سناریوهایی مانند فروش توکن، پرداخت هزینه خدمات، یا مدیریت کیف پول های هوشمند کاملاً ضروری است.
آموزش توابع Payable در سالیدیتی
تا اینجای کار، ما از توکن ها برای نمایش مقدار استفاده میکردیم، اما اگر بخواهیم مستقیماً با اتر (Ether) کار کنیم چه؟ بیایید بررسی کنیم که یک قرارداد هوشمند چگونه میتواند با اتر تعامل داشته باشد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
contract ExampleContract { function payMe() public payable { } function howMuchEtherIHave() public view returns (uint256) { return address(this).balance; } } |
بعد از اینکه این قرارداد را در محیط Remix مستقر (Deploy) کردید، متوجه میشوید که دکمه مربوط به تابع payMe
به رنگ قرمز درمیآید. این به این معناست که میتوانید هنگام فراخوانی این تابع، مقداری اتر (Ether) به آن ارسال کنید. فیلدی در بالای دکمه ظاهر میشود که میتوانید مقدار دلخواه را وارد نمایید.
با استفاده از تابع howMuchEtherIHave
میتوانید بررسی کنید که چه مقدار اتر در آدرس این قرارداد ذخیره شده است.
شما میتونید اتر (Ether) را در واحدهای مختلفی مانند Wei، Gwei، Finney یا خود Ether ارسال کنید.
-
۱ Wei برابر است با یک ده میلیارد میلیاردُم (1/10^18) اتر.
-
۱ Gwei برابر است با یک میلیاردم اتر.
-
۱ Finney برابر است با یک دهم اتر.
برای ساده شدن کار، فرض کنیم میخواهیم یک اتر ارسال کنیم.
وقتی تابع howMuchEtherIHave
را اجرا میکنیم، عدد زیر به ما برمیگردد:
1000000000000000000
این به این معنا نیست که ما به طور جادویی مقدار زیادی اتر ایجاد کردهایم!
بلکه چون در بلاکچین چیزی به نام عدد اعشاری (float) وجود ندارد، اتر هم مثل توکن های ERC20 برای نمایش اعشار از واحدهای کوچکتر استفاده میکند.
در واقع یک واحد «اتر» برابر با ۱۰ به توان ۱۸ وی (Wei) است.
راستی، ساختار .balance
نهتنها برای قرارداد فعلی، بلکه برای هر آدرسی قابل استفاده است. یعنی یک قرارداد هوشمند میتواند بررسی کند که یک آدرس چقدر اتر دارد.
مثلاً با کدی مثل زیر میتوانیم موجودی یک آدرس را ببینیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function howMuchEtherYouHave() public view returns (uint256) { return msg.sender.balance; } function howMuchEtherTheyHave(address them) public view returns (uint256) { return them.balance; } |
اگر به یک تابعی که دارای کلیدواژه payable
نیست، مقدار 10000000000000000000
(یعنی ۱۰ اتر) ارسال کنید، تراکنش با خطا مواجه شده و بازمیگردد (revert).
دلیل این محدودیت چیست؟
ممکن است این پرسش پیش بیاید که چرا نباید اجازه دهیم هر تابعی اتر دریافت کند؟ به ویژه اگر کاربری تمایل به ارسال آن دارد. پاسخ این است که در طراحی قراردادهای هوشمند، وضوح و محدودیت در رفتار توابع از اهمیت بالایی برخوردار است. هر تابع باید رفتاری دقیق، مشخص و قابل پیشبینی داشته باشد.
اگر تابعی برای دریافت اتر طراحی نشده باشد، دریافت وجه از بیرون میتواند منجر به رفتارهای ناخواسته یا مبهم شود. بنابراین، وجود کلیدواژه payable
بهعنوان یک شرط لازم برای دریافت اتر، به حفظ امنیت و پیشبینیپذیری قرارداد کمک میکند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function moreThanOneEtherV1() public view returns (bool) { if (msg.sender.balance > 1 ether) { return true; } return false; } function moreThanOneEtherV2() public view returns (bool) { if (msg.sender.balance > 10**18) { return true; } return false; } |
ساختار Payable در سازنده (Constructor)
در سالیدیتی، این امکان وجود دارد که سازنده (Constructor) را نیز با کلیدواژه payable
تعریف کنیم. این قابلیت زمانی کاربرد دارد که بخواهید قرارداد هوشمند شما در لحظه ایجاد، با دریافت اتر (Ether)، فعالیت خود را با امتیاز و برتری اولیه آغاز کند. البته توجه داشته باشید که ارسال اتر هنگام ساخت قرارداد باید بهصورت صریح انجام شود؛ صرفاً payable
بودن سازنده کافی نیست.
همچنین، صرفاً payable
بودن یک تابع به این معنا نیست که الزاماً باید در هر بار فراخوانی آن، اتر ارسال شود. بلکه این تنها یک اجازه برای امکان دریافت اتر است.
ارسال اتر بین قراردادهای هوشمند در سالیدیتی
ارسال اتر (Ether) از طریق محیط Remix بسیار ساده است، زیرا میتوانید هنگام اجرای تابع، مقدار اتر را مستقیماً مشخص کنید.
اما اگر یک قرارداد هوشمند بخواهد به قرارداد دیگری اتر ارسال کند، چه باید کرد؟
در چنین حالتی باید از تابع call
که پیشتر با آن آشنا شدیم استفاده کنید،
با این تفاوت که این بار یک آرگومان اضافی (که شبیه یک آبجکت متا است) نیز در میان خواهد بود.
ممکن است در ابتدا این ساختار کمی عجیب به نظر برسد، اما بهمرور زمان به آن عادت خواهید کرد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
contract ReceiveEther { function takeMoney() public payable { } function myBalance() public view returns (uint256) { return address(this).balance; } } contract SendMoney { constructor() payable { } function sendMoney(address receiveEtherContract) public payable { uint256 amount = myBalance(); (bool ok, ) = receiveEtherContract.call{value: amount}( abi.encodeWithSignature("takeMoney()") ); require(ok, "transfer failed"); } function myBalance() public view returns (uint256) { return address(this).balance; } } |
بیایید بخشهای مختلف این ساختار را بررسی کنیم:
-
در اینجا، تابع
call
دارای یک شیء شبیه به JSON میان نام تابع و آرگومان ها است. این همان جایی است که مقدار اتر (Ether) برای ارسال مشخص میشود. کلید"value"
تعیینکننده مقدار ارسالی است و بهصورت پیشفرض صفر است. -
در قسمت
(bool ok,)
، ما یک تاپل تعریف کردهایم که مقدار دوم آن خالی گذاشته شده است. این بدان معناست که ما خروجی تابعtakeMoney()
را نادیده میگیریم. اگر نیاز به بررسی مقدار بازگشتی نداریم، از این ساختار استفاده میکنیم. -
با این حال، همچنان برای ما مهم است که آیا انتقال با موفقیت انجام شده یا نه. به همین دلیل از
require(ok)
استفاده کردهایم تا در صورت شکست انتقال، تراکنش بازگردانده شود.
چند آزمایش پیشنهادی:
-
هر دو قرارداد را مستقر (Deploy) کنید، اما هنگام ساخت قرارداد
SendMoney
، مقداری اتر برای آن در نظر بگیرید. سپس مقدارmyBalance
را در هر دو قرارداد، قبل و بعد از فراخوانیsendMoney
بررسی کنید. -
عبارت
payable
را از تابعtakeMoney
حذف کنید و نتیجه را ببینید. انتظار میرود تراکنش برگشت بخورد (Revert شود). -
هنگام فراخوانی
sendMoney
مقداری اتر ارسال کنید و ببینید چگونه موجودیreceiveEther
افزایش مییابد.
محدودیت توابع Payable در سالیدیتی
توابعی که با payable
تعریف میشوند نمیتوانند از نوع view
یا pure
باشند.
دلیل این امر آن است که تغییر موجودی اتر یک قرارداد هوشمند، نوعی تغییر وضعیت (state change) در بلاکچین محسوب میشود. این تغییر، دائمی و غیرقابل بازگشت است؛ مانند تغییر یک متغیر ذخیره شده در حافظه. بنابراین، کامپایلر سالیدیتی اجازه نخواهد داد که چنین توابعی بهعنوان view
یا pure
تعریف شوند.
راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.
- انتشار: ۱۵ اردیبهشت ۱۴۰۴
دسته بندی موضوعات
- آموزش ارز دیجیتال
- آموزش برنامه نویسی
- آموزش متنی برنامه نویسی
- اطلاعیه و سایر مطالب
- پروژه برنامه نویسی
- دوره های تخصصی برنامه نویسی
- رپورتاژ
- فیلم های آموزشی
- ++C
- ADO.NET
- Adobe Flash
- Ajax
- AngularJS
- apache
- ARM
- Asp.Net
- ASP.NET MVC
- AVR
- Bootstrap
- CCNA
- CCNP
- CMD
- CSS
- Dreameaver
- EntityFramework
- HTML
- IOS
- jquery
- Linq
- Mysql
- Oracle
- PHP
- PHPMyAdmin
- Rational Rose
- silver light
- SQL Server
- Stimulsoft Reports
- Telerik
- UML
- VB.NET&VB6
- WPF
- Xml
- آموزش های پروژه محور
- اتوکد
- الگوریتم تقریبی
- امنیت
- اندروید
- اندروید استودیو
- بک ترک
- بیسیک فور اندروید
- پایتون
- جاوا
- جاوا اسکریپت
- جوملا
- دلفی
- دوره آموزش Go
- دوره های رایگان پیشنهادی
- زامارین
- سئو
- ساخت CMS
- سی شارپ
- شبکه و مجازی سازی
- طراحی الگوریتم
- طراحی بازی
- طراحی وب
- فتوشاپ
- فریم ورک codeigniter
- فلاتر
- کانستراکت
- کریستال ریپورت
- لاراول
- معماری کامپیوتر
- مهندسی اینترنت
- هوش مصنوعی
- یونیتی
- کتاب های آموزشی
- Android
- ASP.NET
- AVR
- LINQ
- php
- Workflow
- اچ تی ام ال
- بانک اطلاعاتی
- برنامه نویسی سوکت
- برنامه نویسی موبایل
- پاسکال
- پایان نامه
- پایتون
- جاوا
- جاوا اسکریپت
- جی کوئری
- داده کاوی
- دلفی
- رباتیک
- سئو
- سایر کتاب ها
- سخت افزار
- سی اس اس
- سی پلاس پلاس
- سی شارپ
- طراحی الگوریتم
- فتوشاپ
- مقاله
- مهندسی نرم افزار
- هک و امنیت
- هوش مصنوعی
- ویژوال بیسیک
- نرم افزار و ابزار برنامه نویسی
- وردپرس