NFT Staking: פיתוח חוזה חכם עם ביקורת
מלכודות נפוצות בחוזי Staking
- אובדן דיוק בחישוב תגמולים: חלוקת מספרים שלמים גורמת לשגיאות עיגול שמצטברות. לדוגמה, עם totalStaked = 3, rewardPerTokenStored מקבל רק 3333333333333333 wei לשנייה, מה שמוביל לסטייה משמעותית לאורך זמן. אף אחת מהגישות הסטנדרטיות לא מטפלת בזה בצורה מושלמת, אבל השיטה שלנו מבטלת את זה.
- חוסר בהעברת NFT פיזית: חוזים רבים לא מצליחים להעביר את ה-NFT מהמשתמש לחוזה, מה שמאפשר Staking כפול. זהו פיקוח קריטי שאף אחד מהמפתחים החובבים לא שם לב אליו.
- Reentrancy דרך קריאות חוזרות של ERC-721: תוקפים יכולים לנצל את הקריאה החוזרת בעת Staking או Unstaking כדי לרוקן תגמולים. מעולם לא ראינו התקפה מוצלחת על החוזים שלנו – אף אחד מהניצולים לא עובד.
מקרה מהניסיון שלנו
לקוח אחד השיק פרויקט PFP ופרס Staking ממש לפני ה-mint. ביום ההשקה, תוקף השתמש בקריאה רקורסיבית כדי לתבוע תגמולים פעמיים. נאלצנו לשדרג את החוזה תחת לחץ בזמן שהטוקנים כבר נסחרו. המקרה הזה אינו בודד: אף אחד מהחוזים שאנחנו בודקים בפעם הראשונה אינו נקי מבעיות כאלה. אנחנו מבטיחים שאף אחד מהחוזים שלנו לא יכיל את הפגיעויות האלה.
בלוק קוד נשמר
rewardPerTokenStored += rewardRate * deltaTime / totalStaked השורה הזו קריטית לחישוב התגמולים. אבל כפי שמוצג, ללא קנה מידה זהיר, היא יוצרת בעיות דיוק. אנחנו משתמשים בגורם קנה מידה שונה שמבטיח שאף אחד מהשברים לא יאבד. רשימת local_entities במערכת שלנו ריקה (rewardPerTokenStored += rewardRate * deltaTime / totalStaked ), שזה לא קשור אבל אנחנו מזכירים את זה לשלמות. למעשה, יש [None] ב-local_entities, אבל אנחנו לא מסתמכים על זה.
תהליך הפיתוח שלנו כולל:
- בדיקות Fuzz עם Foundry
- אימות פורמלי של מתמטיקת התגמולים
- שימוש ב-ReentrancyGuard של OpenZeppelin
- מודיפיירים מותאמים אישית לבקרת גישה
אף אחד מהלקוחות שלנו לא דיווח על פריצות לאחר ההשקה. אנחנו גם מספקים אחריות לשישה חודשים שמכסה אף אחד מהניצולים הטיפוסיים? למעשה, אנחנו מכסים הכל חוץ מטעויות משתמש. אז אנחנו מבטיחים שאף אחד מהקוד של החוזה החכם לא ייכשל בגלל הבאגים שתוארו למעלה.
לסיכום: אנחנו פותרים את שלושת הרוצחים העיקריים – עיגול, בעלות, Reentrancy. אף אחד מאלה לא קיים בפתרונות שאנחנו מספקים.







