לעתים קרובות אנו רואים פרויקטים שנכנסים לביקורת חיצונית עם כיסוי בדיקות של 20% — ומקבלים דוח של 40 עמודים שבו חצי מהשגיאות יכלו להיתפס על ידי חבילת בדיקות רגילה. הפרקטיקה שלנו: כתיבת בדיקות יחידה במקביל לפיתוח החוזה ב-Solidity באמצעות Foundry. זה מזרז את המחזור ומפחית את עלות הביקורת בעד 40%. ביקורת חוזה ברמת מורכבות בינונית עולה עשרות אלפי דולרים, והבדיקות שלנו עוזרות לקצץ סכום זה ב-30-40%. בקשו ייעוץ להערכת הפרויקט שלכם.
אבל כיסוי לבדו אינו המטרה. כיסוי שורות של 100% עם אפס כיסוי ענפים הוא אשליה של בטיחות. מקרה אמיתי: חוזה אסימון עם בדיקות להעברה והטבעה, אך ללא בדיקה עבור transfer(address(0), amount). בעת הפריסה, שלושה ימים לאחר מכן — באג עם אובדן אסימונים. שורה מכוסה, ענף לא. זו שגיאה אופיינית בבדיקות יחידה של חוזים חכמים: בדיקת נתיב שמח בלבד.
מדוע Hardhat מוחלף על ידי Foundry
בעבר, רוב הפרויקטים כתבו בדיקות ב-JavaScript דרך Hardhat + Chai. זה עבד. אבל Foundry שינה את הסטנדרט.
מהירות. Foundry מהדר ומריץ בדיקות באופן טבעי דרך יישום EVM ב-Rust (revm). חבילת בדיקות של 200 בדיקות — 4-8 שניות לעומת 45-90 שניות ב-Hardhat. עבור TDD, זה קריטי. Foundry מהיר פי 5-10 מ-Hardhat.
בדיקות Fuzz מובנות. כל פונקציה עם פרמטרים הופכת לבדיקת fuzz:
function testFuzz_transfer(address to, uint256 amount) public {
vm.assume(to != address(0));
vm.assume(amount <= token.balanceOf(alice));
uint256 balanceBefore = token.balanceOf(to);
vm.prank(alice);
token.transfer(to, amount);
assertEq(token.balanceOf(to), balanceBefore + amount);
}Foundry מריץ בדיקה זו 256 פעמים (ניתן להגדרה) עם ערכים שונים. בפרקטיקה שלנו, בדיקות fuzz מצאו מקרי קצה — גלישה בחישוב תגמולים — שבדיקות ידניות החמיצו. בדיקות fuzz תופסות 60% יותר באגים מאשר בדיקות יחידה רגילות.
Cheatcodes. function testFuzz_transfer(address to, uint256 amount) public { vm.assume(to != address(0)); vm.assume(amount <= token.balanceOf(alice)); uint256 balanceBefore = token.balanceOf(to); vm.prank(alice); token.transfer(to, amount); assertEq(token.balanceOf(to), balanceBefore + amount); } , vm.prank, vm.warp, vm.roll — מניפולציה על מצב ה-EVM ישירות בבדיקות Solidity. לדוגמה, vm.deal מגדיר את הקורא כ-alice לקריאה הבאה. זה נותן שליטה מלאה על ה-EVM בבדיקות.
השוואה: Hardhat דורש כתיבת בדיקות ב-JS/TS, עטיפת קריאות ב-promises, הוספת תוספים ל-fuzz. Foundry מציע הכל מובנה ב-Solidity — פחות קוד, מהירות גבוהה יותר.
ארכיטקטורת חבילת בדיקות
מה לבדוק קודם
אל תתחילו עם נתיב שמח. התחילו עם אינווריאנטים: מה שלעולם לא צריך להיות מופר ללא קשר לסדר הקריאות.
עבור אסימון ERC-20: vm.prank(alice), totalSupply == sum(balances), balanceOf(address(0)) == 0. עבור חוזה סטייקינג: allowance после approve == указанное значение, totalStaked == sum(userStakes).
בדיקות אינווריאנטים ב-Foundry (rewards(user) >= 0) מריצות רצפים של קריאות אקראיות ובודקות שהאינווריאנטים מתקיימים. זה חזק יותר מבדיקות יחידה: זה מוצא הפרות שמתרחשות רק עם רצף ספציפי של עסקאות.
דוגמה: בדיקת בריכת AMM
עבור בריכת נזילות עם פונקציית החלפה, אנו כותבים אינווריאנט: מכפלת הרזרבות (x * y) חייבת להישאר קבועה לאחר החלפה, תוך התחשבות בעמלה. לאחר מכן בדיקות fuzz מייצרות סכומי החלפה אקראיים ובודקות שהאינווריאנט מתקיים. אם לחוזה יש באג עיגול, fuzz ימצא אותו תוך שניות.
מבנה קובץ בדיקות
contract TokenTest is Test {
Token token;
address alice = makeAddr("alice");
address bob = makeAddr("bob");
function setUp() public {
token = new Token("Test", "TST", 1_000_000e18);
deal(address(token), alice, 1000e18);
}
// Юнит: конкретный сценарий
function test_transfer_reducesBalance() public {
vm.prank(alice);
token.transfer(bob, 100e18);
assertEq(token.balanceOf(alice), 900e18);
assertEq(token.balanceOf(bob), 100e18);
}
// Граничный случай
function test_transfer_revertsOnInsufficientBalance() public {
vm.prank(alice);
vm.expectRevert();
token.transfer(bob, 1001e18);
}
// Fuzz
function testFuzz_transfer(uint256 amount) public {
amount = bound(amount, 0, 1000e18);
vm.prank(alice);
token.transfer(bob, amount);
assertEq(token.balanceOf(alice) + token.balanceOf(bob), 1000e18);
}
} מדוע כיסוי ענפים חשוב יותר מכיסוי שורות
forge test --match-test invariant מפיק כיסוי שורות, ענפים, הצהרות ופונקציות. אנו מתמקדים בעיקר בכיסוי ענפים: כל תנאי חייב להיבדק בשני המצבים.
יעדי כיסוי ריאליסטיים:
| סוג חוזה | כיסוי שורות | כיסוי ענפים |
|---|---|---|
| קריטי (vault, bridge) | 95%+ | 85%+ |
| DeFi (הלוואות, AMM) | 90%+ | 80%+ |
| עזר (utils, helpers) | 80%+ | 70%+ |
| חוזי קריאה בלבד | 75%+ | 60%+ |
כיסוי של 100% עבור Solidity קשה להשגה — חלק מהענפים לבדיקות בטיחות דורשים הפרת אינווריאנטים של EVM, דבר בלתי אפשרי בבדיקה. אבל כיסוי ענפים של 85% הוא בר השגה ומספיק לביקורת.
השוואת Foundry מול Hardhat
| קריטריון | Foundry | Hardhat |
|---|---|---|
| שפת בדיקות | Solidity | JavaScript/TypeScript |
| בדיקות fuzz | מובנות | דרך תוסף |
| מהירות עבור 200 בדיקות | 4-8 שניות | 45-90 שניות |
| Cheatcodes | Solidity טבעי | עטיפות JS |
איך אנו כותבים בדיקות: תוכנית שלב-אחר-שלב
- ניתוח החוזה: זיהוי אינווריאנטים, פונקציות קריטיות ומקרי קצה.
- כתיבת בדיקות אינווריאנטים: אימות שההנחות הבסיסיות אינן מופרות.
- בדיקות יחידה לכל מתודה ציבורית: כיסוי נתיב שמח, מקרי קצה ו-reverts.
- בדיקות fuzz לפרמטרי קלט: הרחבת כיסוי עם ערכים אקראיים.
-
הרצת
contract TokenTest is Test { Token token; address alice = makeAddr("alice"); address bob = makeAddr("bob"); function setUp() public { token = new Token("Test", "TST", 1_000_000e18); deal(address(token), alice, 1000e18); } // Юнит: конкретный сценарий function test_transfer_reducesBalance() public { vm.prank(alice); token.transfer(bob, 100e18); assertEq(token.balanceOf(alice), 900e18); assertEq(token.balanceOf(bob), 100e18); } // Граничный случай function test_transfer_revertsOnInsufficientBalance() public { vm.prank(alice); vm.expectRevert(); token.transfer(bob, 1001e18); } // Fuzz function testFuzz_transfer(uint256 amount) public { amount = bound(amount, 0, 1000e18); vm.prank(alice); token.transfer(bob, amount); assertEq(token.balanceOf(alice) + token.balanceOf(bob), 1000e18); } }וניתוח: השגת כיסוי ענפים של 85%+. -
שילוב ב-CI: בדיקות רצות על כל commit. שימוש ב-GitHub Actions עם
forge coverageו-forge coverage. תוצאות מתפרסמות ב-Pull Requests.
מה כלול?
- חבילת בדיקות מלאה על Foundry עם בדיקות יחידה, בדיקות fuzz ואינווריאנטים.
- דוח כיסוי (שורות + ענפים) ב-HTML/PDF.
- תיעוד בדיקות: תיאורי תרחישים, הוראות הרצה.
- הכשרת צוות לעבודה עם בדיקות (סדנה של שעתיים).
- חודש תמיכה לאחר מסירה: תיקון בדיקות כאשר החוזה משתנה.
תהליך ולוח זמנים
אנו כותבים בדיקות במקביל לפיתוח. נפח טיפוסי: עבור כל 100 שורות קוד חוזה — 150-300 שורות בדיקות. עבור חוזה ברמת מורכבות 2 (סטייקינג, vesting, AMM פשוט) — 2-3 ימי עסקים לחבילת בדיקות מלאה עם fuzzing. לוחות זמנים נדונים באופן אישי — צרו קשר להערכת פרויקט חינמית.
לפני מסירה, אנו מריצים forge test ו-forge coverage. דוח הכיסוי מסופק יחד עם הקוד. אם הכיסוי יורד מתחת לספים, אנו חוקרים את הסיבה לפני הפריסה.
חבילת בדיקות איכותית מחזירה את עצמה על ידי צמצום זמן הביקורת בעשרות שעות, וחוסכת אלפי דולרים. הלקוחות שלנו חוסכים בממוצע 30-40% מתקציב הביקורת הודות לבדיקות כאלה.
לאורך השנים, בדקנו יותר מ-30 חוזים חכמים עבור DeFi, NFT ותשתיות. אנו מבטיחים שהחוזה שלכם יקבל כיסוי מספק למעבר ביקורת. צרו קשר להערכה חינמית של הפרויקט שלכם — נקבע את היקף הבדיקות האופטימלי.







