אחרי שלמדנו את ההגדרות הבסיסיות לפונקציות, והתחלתם גם לרשום פונקציות בעצמכם ופתאום נתקלתם בהגדרה נוספת שלא הכרתם שהיא: modifier . modifier הינה הגדרה מיוחדת בעלת כח רב, אך אינה קשורה ל visibility modifiers שעליהם דיברנו בחלק העליון.
modifier
יש לציין שאין ל modifier קשר ישיר לפונקציות אם כי תמיד הוא משויך לפונקציות של החוזה עצמו אך כוונתי היא שאינו חלק מההגדרות של פונקציה. כל modifier הוא בהגדרתו internal ולכן יעשה בו שימוש רק לטובת החוזה וליורשיו, modifier שימושית בתוך פונקציות ולפעמים נראה כמו פונקציה בעצמה , יכולה לקבל arguments כמו פונקציה ,אבל לא מחייב כלל. הגדרה זו מאפשר לנו לעשות שימוש חוזר שיהפוך את הכתיבה שלנו לפונקציונלית יותר ודינאמית יותר.
כעת אציג דוגמה ונחקור אותה על מנת להבין את הגדרתה ואת היכולות הטמונים modifiers:
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; contract Test { uint constant FAV_NUMBER = 10; uint price; constructor() {} modifier costs(uint _price) { require(FAV_NUMBER >= _price, "error"); _; } function calculateCosts(uint _price) external costs(_price) { price = _price; } }
הבאתי דוגמה פשוטה בעלת משמעות מינורית, על מנת רק להסביר את התחביר של modifier , אך בהמשך הפוסט אביא עוד דוגמאות יותר פרקטיות ותראו כמה שימושי ועוצמתי modifier.
אז כפי שניתן לראות את modifier אנחנו נגדיר בחלק העליון של החוזה, לרוב בחוזים תגלו שיש המון modifies וכולם מוגדרים בתחילת החוזה ובהם נעשים שימושיים רבים במהלך החוזה, ניתן לראות שהגדרת המודיפייר הינה דומה לפונקציה , אך אין אנו מוסיפים מאפיינים , המאפיין היחיד של modifier הוא internal שאינו ניתן לשינוי. שימו לב שהקוד שמופיע בmodifier כרגע הוא תנאי, require , ולאחריו קיים קו תחתון (ונקודה פסיק כמובן, כמו בכל שורה), הקו הזה תמיד יופיע בכל modifier כי אחרת אין ממש היגיון להשתמש ב modifier. מיקומו של קו תחתון זה יכול להיות במיקומים שונים בתוך modifier ומה שהוא אומר זה שאם תגדירו פונקציה שמשתמשת בmodifier זה, הקוד של פונקציה זו ירוץ מאותה שורה שמופיע הקו התחתון , כלומר בדוגמה שלנו, קודם ירוץ התנאי , ולאחר מכן ירוץ קוד הפונקציה, כלומר מה שיקרה כאן, זה שאם הפרמטר שיכנס לפונקציה יהיה פחות מ10 אז המשתנה price לא ישנה את ערכו. כמו כן , שימו לב שmodifier יכול לקבל אגרומנטים אך לא מחייב.
שימושים נפוצים ודוגמאות של modifier
אחד השימושים הנפוצים שבטח רובכם ראיתם הם onlyOwner שמוסיפים לפונקציות שונות בחוזה שרוצים שרק הבעלים של החוזה יכול לעשות, למעשה מדובר ב modifier פשוט שנראה כך:
modifier onlyOwner() { require(msg.sender == owner); _; }
אפשר לראות שבמקרה זה modifier לא מקבל שום אגרומנטים.
אפשר להגדיר מספר לא מוגבל של modifiers לפונקציה, שימוש לב לשרשור הבא בדוגמה זו:
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; contract Test { uint constant FAV_NUMBER = 10; uint price; constructor() {} modifier costs(uint _price) { require(FAV_NUMBER >= _price, "error"); _; } modifier onlyOwner() { require(msg.sender == owner); _; } function calculateCosts(uint _price) external costs(_price) onlyOwner { price = _price; } }
דוגמה זו מעניינת ואולי יכולה לבלבל מעט אז שימו לב לסדר של הדברים ואיך מתנהגים הקווים התחתונים במקרה הזה:
- המודיפייר הראשון שירוץ יהיה costs
- המודיפייר השני שירוץ יהיה onlyOwner
- רק במודיפייר האחרון ירוץ הקוד של הפונקציה בהתאם למיקום של הקו התחתון.
למעשה כל המודיפיירים בשרשור לא יריצו את קוד הפונקציה אלא רק המודיפייר האחרון יעשה זאת.
דוגמה נוספת שיכולה להיות מעניינת, נסו לחשוב מה יקרה במקרה הזה, או אפילו נסו להריץ בעצמכם עם רמיקס.
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; contract Test { uint public modState1; uint public modState2; uint public modState3; constructor() {} modifier modA() { modState1 = modState1 + 1; _; } modifier modB() { modState2 = modState2 + 1; _; modState2 = modState2 + 1; _; } function func() public modA modB { modState3 = modState3 + 1; } }
במקרה זה תגלו כי קוד הפונקציה func ירוץ פעמיים בהתאם למיקום של הקו התחתון. כמו כן המודיפייר הראשון modA ירוץ לפני modB כפי שהסברתי בדוגמה הקודמת.
לסיכום קורס סולידיטי – modifiers
השימוש בmodifiers הוא חיוני ופרקטי מאד, עשו בו שימוש נבון על לפשט לכם את הכתיבה ולייעל אותה. כמו כן בהמשך הקורס תראו שימושים עם modifiers כך שעם עוד לא השתכנעתם ביתרונותיו , אני מקווה שתראו זאת בהמשך הקורס וכן תאמצו שימוש נכון עם modifiers. בהצלחה ונתראה שפוסט הבא 🐊😘