کاربرد Modifierها در Solidity

Modifierها توابعی هستند که بر روی سایر توابع اعمال می‌شوند و از آنها می‌توان برای چک‌کردن بعضی پیش‌نیازها مانند کنترل سطوح دسترسی قبل از اجرا شدن سایر توابع استفاده کرد. فرض کنید تابعی داریم که فقط ایجادکننده یا مالک قرارداد هوشمند اجازه فراخوانی آن را داشته باشد. به همین منظور به دو روش زیر این کار را می‌توان انجام داد:

روش اول: if-else

function increment() public {
    if (owner != msg.sender) {
         return;
    }
    count = count + 1;
}

روش دوم: modifier

modifier isOwner() {
     require(msg.sender == owner);
     _;
}

function increment() public isOwner {
     count = count + 1;
}
همانطور که در قطعه کد بالا مشاهده می‌کنید، modifierها دقیقا مشابه توابع تعریف می‎‌شوند. در تعریف isOwner، تابع require شرطی که درون پرانتز آن مشخص شده است بررسی می‌کند، اگر شرط برقرار نباشد یک exception اتفاق می‌افتد و اجراء قرارداد خاتمه می‌یابد. در غیر اینصورت کلمه کلیدی” _ ” به کامپایلر می‌گوید که “_” را با کد تابعی که modifier بر روی آن اعمال شده است، جایگزین کند.

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

modifierها همچنین مانند توابع می‌توانند پارامتر ورودی هم داشته باشند. به عنوان مثال، فرض کنید در قرارداد هوشمندمان بخواهیم کاربران متعدد و با سطوح دسترسی مختلف داشته باشیم. برای اینکار می‌توان آدرس حساب کاربر را به عنوان ورودی در modifier دریافت کرد.

modifier onlyBy(address _account) {
    require(msg.sender == _account);
    _;
}

function increment() public onlyBy(owner) {
    count = count + 1;
}
ضمنا می‌توان بر روی یک تابع چندین modifier و هر کدام را چندین بار اعمال کرد، به عنوان مثال:
modifier onlyIf(bool _condition) {
    require(_condition);
    _;
}

function increment() public onlyIf(msg.sender == owner) onlyIf(count < 200) {
    count = count + 1;
}

در ادامه یک مثال کامل از قراردادی که در آن از modifier استفاده شده است مشاهده می‌کنید، که می‌توان آن را در سایت remix.ethereum.org تست کرد:

pragma solidity ^0.4.0;

contract ModifierContract {

   address owner;
   uint count = 0;

   function ModifierContract() public {
       owner = msg.sender;
   }

   modifier isOwner() {
       require(msg.sender == owner);
       _;
   }

   function increment() public isOwner {
       count = count + 1;
   }

   function getCount() public constant returns(uint) {
       return count;
   }
}
چند نکته و کلمه کلیدی در کد بالا وجود دارد که لازم می‌دانم توضیحاتی در مورد آنها بدهم:

msg.sender: تعدادی متغیر و تابع از پیش تعریف شده در قراردادهای هوشمند همیشه وجود دارند که به صورت سراسری (global) تعریف شده‌اند و حاوی اطلاعات مختلفی در مورد بلاک‌چین هستند. یکی از این متغیرها msg.sender است که آدرس حساب اتریوم شخص یا قراردادی است که قرارداد فعلی را فراخوانی کرده است.

address: یکی از انواع داده تعریف شده در زبان solidity است که اندازه آن ۲۰ بایت است و برای تعریف متغیرهایی که آدرس‌های اتریوم را نگهداری می‌کنند، استفاده می‌شود.

uint: نوع داده عدد صحیح بدون علامت است.

هنگامی که برای اولین بار قرارداد فوق بر روی شبکه اتریوم ایجاد می‌شود، آدرس ایجادکننده قرارداد در constructor به متغیر owner اختصاص داده می‌شود. هر زمان حساب‌کاربری کسی غیر از ایجادکننده قرارداد تابع increment را فراخوانی کند، یک exception اتفاق می‌افتد و اجراء قرارداد خاتمه می‌یابد.

برای بررسی آن کافیست مشابه تصویر زیر با یک حساب کاربری (فیلد Account) قرارداد را ایجاد و با حساب کاربری دیگری تابع increment را فراخوانی کنید، تا نتیجه کار را مشاهده نمائید.

درصورتی که تاکنون از IDE آنلاین remix.ethereum.org استفاده نکرده‌اید، پیشنهاد می‌کنم مطلب اولین قرارداد هوشمند با Solidity را مطالعه کنید، تا با کاربرد هرکدام از این فیلدها و نحوه ایجاد و تست قرارداد سریعتر آشنا شوید.

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now
Logo
Center