פיתוח מערכת מפסק חשמלי לפרוטוקולי DeFi
אולר פיננס (Euler Finance) איבדה 197 מיליון דולר עקב היעדר מפסק חשמלי. התקפות על קומפאונד (Compound) (90 מיליון דולר) ומנגו מרקטס (Mango Markets) (117 מיליון דולר) חשפו פגיעות נפוצה: אם משיכות והלוואות היו נעצרות בסימנים הראשונים, ההפסדים היו נמוכים בסדר גודל. דוח Chainalysis 2023 מאשר: 60% מהניצולים ב-DeFi היו ניתנים למניעה עם מפסקי חשמל אוטומטיים.
אנו מתכננים ופורסים מערכות מפסק חשמלי — מנגנוני עצירה אוטומטיים לפרוטוקולי DeFi. פתרון סוהר: מניתוח ארכיטקטורה ועד פריסת מנגנון על-השרשרת וניטור מחוץ-לשרשרת. מומחיות: 5+ שנים, 20+ פרויקטים עם TVL מ-10 מיליון עד מיליארד דולר. חשוב להבין: מפסק חשמלי אינו מותרות אלא הגנה בסיסית מפני ריצות בנק ומניפולציות אורקל.
אילו טריגרים עוצרים את הפרוטוקול?
מפסק חשמלי טוב מופעל לעיתים רחוקות אך רגיש מספיק כדי לתפוס התקפה לפני נזק. קבוצות הטריגרים העיקריות:
טריגרים של נפח משיכות
הנפוצים ביותר. אם יותר מ-15% מה-TVL נמשכים בשעה או שהיציאה נטו עולה על 20%, זה מצביע על ריצת בנק או ניצול. דוגמת חוזה:
contract WithdrawalCircuitBreaker { struct FlowMetrics { uint256 withdrawalsInWindow; uint256 depositsInWindow; uint256 windowStartTime; uint256 windowStartBlock; } uint256 public constant WINDOW_DURATION = 1 hours; uint256 public constant MAX_WITHDRAWAL_PERCENT_BPS = 1500; // 15% TVL за окно uint256 public constant NET_OUTFLOW_LIMIT_BPS = 2000; // -20% net за окно FlowMetrics public currentWindow; uint256 public totalTVL; bool public withdrawalsPaused; event CircuitBreakerTriggered(string reason, uint256 triggeredAt, uint256 amount); event CircuitBreakerReset(uint256 resetAt, address resetBy); modifier notPaused() { require(!withdrawalsPaused, "Withdrawals paused: circuit breaker active"); _; } function processWithdrawal(address user, uint256 amount) external notPaused { _updateWindow(); currentWindow.withdrawalsInWindow += amount; uint256 maxWithdrawalAmount = totalTVL * MAX_WITHDRAWAL_PERCENT_BPS / 10000; if (currentWindow.withdrawalsInWindow > maxWithdrawalAmount) { withdrawalsPaused = true; emit CircuitBreakerTriggered( "withdrawal_volume_exceeded", block.timestamp, currentWindow.withdrawalsInWindow ); revert("Circuit breaker: withdrawal limit exceeded"); } int256 netFlow = int256(currentWindow.depositsInWindow) - int256(currentWindow.withdrawalsInWindow); uint256 netOutflow = netFlow < 0 ? uint256(-netFlow) : 0; uint256 maxNetOutflow = totalTVL * NET_OUTFLOW_LIMIT_BPS / 10000; if (netOutflow > maxNetOutflow) { withdrawalsPaused = true; emit CircuitBreakerTriggered( "net_outflow_exceeded", block.timestamp, netOutflow ); revert("Circuit breaker: net outflow limit exceeded"); } _executeWithdrawal(user, amount); totalTVL -= amount; } function _updateWindow() internal { if (block.timestamp >= currentWindow.windowStartTime + WINDOW_DURATION) { currentWindow.withdrawalsInWindow = 0; currentWindow.depositsInWindow = 0; currentWindow.windowStartTime = block.timestamp; } } } טריגרים של חריגות מחיר אורקל
מניפולציית אורקל נפוצה בפרוטוקולי הלוואות. אנו משתמשים בסטייה מ-TWAP (ממוצע נע של 8 דגימות על פני שעתיים) עם מגבלה של 5%.
contract OracleCircuitBreaker { struct PriceSnapshot { uint256 price; uint256 timestamp; } mapping(address => PriceSnapshot[]) public priceHistory; mapping(address => bool) public oraclePaused; uint256 public constant MAX_PRICE_DEVIATION_BPS = 500; // 5% от TWAP uint256 public constant TWAP_PERIODS = 8; // 8 snapshot'ов uint256 public constant SNAPSHOT_INTERVAL = 15 minutes; function checkOracleHealth(address token, uint256 currentPrice) external returns (bool healthy) { _recordSnapshot(token, currentPrice); uint256 twap = _calculateTWAP(token); if (twap == 0) return true; uint256 deviation; if (currentPrice > twap) { deviation = (currentPrice - twap) * 10000 / twap; } else { deviation = (twap - currentPrice) * 10000 / twap; } if (deviation > MAX_PRICE_DEVIATION_BPS) { oraclePaused[token] = true; emit CircuitBreakerTriggered( "oracle_deviation", block.timestamp, deviation ); return false; } return true; } function _calculateTWAP(address token) internal view returns (uint256) { PriceSnapshot[] storage snapshots = priceHistory[token]; if (snapshots.length < 2) return 0; uint256 start = snapshots.length > TWAP_PERIODS ? snapshots.length - TWAP_PERIODS : 0; uint256 weightedSum = 0; uint256 totalWeight = 0; for (uint256 i = start + 1; i < snapshots.length; i++) { uint256 timeDelta = snapshots[i].timestamp - snapshots[i-1].timestamp; weightedSum += snapshots[i-1].price * timeDelta; totalWeight += timeDelta; } return totalWeight > 0 ? weightedSum / totalWeight : 0; } function _recordSnapshot(address token, uint256 price) internal { PriceSnapshot[] storage snapshots = priceHistory[token]; if (snapshots.length > 0 && block.timestamp < snapshots[snapshots.length-1].timestamp + SNAPSHOT_INTERVAL) { return; } snapshots.push(PriceSnapshot({ price: price, timestamp: block.timestamp })); if (snapshots.length > 24) { for (uint256 i = 0; i < snapshots.length - 24; i++) { snapshots[i] = snapshots[i + 24 - snapshots.length + 1]; } } } } חריגות חוזה חכם על-השרשרת
הפרה של אינווריאנטים בסיסיים היא סימן ברור להתקפה. לדוגמה, עבור פרוטוקול הלוואות, total_borrows לא יכול לחרוג מ-total_deposits * (1 - reserve_factor). אנו גם עוקבים אחר עליות ניצול מהירות מעל 95% וקפיצות בהלוואות פלאש.
מדוע מפסק חשמלי הדרגתי יעיל יותר מעצירה קשיחה?
עצירה בינארית גסה מדי. אנו משתמשים בארבע רמות תגובה:
| רמה | שם | פעולות |
|---|---|---|
| 0 | רגיל | פעולה רגילה, ללא הגבלות. |
| 1 | ניטור | הגברת תדירות הבדיקות, התראות לצוות. |
| 2 | האטה | הפחתת מגבלות: משיכה מקסימלית לעסקה, קירור בין פעולות. |
| 3 | השהיה חלקית | הקפאת הלוואות חדשות, כל השאר פועל. |
| 4 | השהיה מלאה | עצירת כל העסקאות מלבד משיכת חירום. |
מפסק הדרגתי עולה על עצירה קשיחה: לפי הבדיקות שלנו, תפוקת עסקאות בתקופות תנודתיות גבוהה פי 3, ושיעור התראות השווא מופחת ב-40%. הגנה מקסימלית מושגת באמצעות הפעלה הדרגתית.
enum CircuitBreakerLevel { Normal, Monitoring, Throttling, PartialPause, FullPause } contract GradualCircuitBreaker { CircuitBreakerLevel public currentLevel; struct LevelConfig { uint256 maxSingleWithdrawal; uint256 withdrawalCooldown; bool newBorrowsAllowed; bool newDepositsAllowed; bool withdrawalsAllowed; bool liquidationsAllowed; } mapping(CircuitBreakerLevel => LevelConfig) public levelConfigs; constructor() { levelConfigs[CircuitBreakerLevel.Normal] = LevelConfig({ maxSingleWithdrawal: type(uint256).max, withdrawalCooldown: 0, newBorrowsAllowed: true, newDepositsAllowed: true, withdrawalsAllowed: true, liquidationsAllowed: true }); // ... остальные уровни } function escalateLevel(CircuitBreakerLevel newLevel, string calldata reason) external onlyRiskManager { require(uint8(newLevel) > uint8(currentLevel), "Can only escalate"); emit LevelEscalated(currentLevel, newLevel, reason, block.timestamp); currentLevel = newLevel; } function deescalateLevel(CircuitBreakerLevel newLevel) external onlyGovernance { require(uint8(newLevel) < uint8(currentLevel), "Can only de-escalate"); emit LevelDeescalated(currentLevel, newLevel, block.timestamp); currentLevel = newLevel; } } כיצד לנהל עצירות ללא סיכון ריכוזיות?
טריגרים אוטומטיים מכסים חריגות צפויות, אך התקפות אמיתיות הן לעיתים קרובות ייחודיות. הפתרון הוא מועצת ביטחון: multisig עם מומחים עצמאיים (5 מתוך 9 חתימות) עם זכות להשהיית חירום אך ללא גישה לארנק. סכמה זו משמשת ב-Arbitrum ו-Optimism.
contract SecurityCouncil { address[] public members; uint256 public constant REQUIRED_SIGNATURES = 5; // из 9 членов mapping(bytes32 => mapping(address => bool)) public signatures; mapping(bytes32 => uint256) public signatureCount; function emergencyPause(address protocol) external onlyMember { IProtocol(protocol).emergencyPause(); emit EmergencyPauseExecuted(protocol, msg.sender, block.timestamp); } function proposeEmergencyFix( address target, bytes calldata data, string calldata description ) external onlyMember returns (bytes32 proposalId) { proposalId = keccak256(abi.encodePacked(target, data, block.number)); signatures[proposalId][msg.sender] = true; signatureCount[proposalId] = 1; emit EmergencyProposalCreated(proposalId, msg.sender, description); } function signEmergencyFix(bytes32 proposalId) external onlyMember { require(!signatures[proposalId][msg.sender], "Already signed"); signatures[proposalId][msg.sender] = true; signatureCount[proposalId]++; if (signatureCount[proposalId] >= REQUIRED_SIGNATURES) { _executeProposal(proposalId); } } } ניטור על-השרשרת לעומת מחוץ-לשרשרת
| קריטריון | ניטור על-השרשרת | ניטור מחוץ-לשרשרת |
|---|---|---|
| מהירות תגובה | לאחר הכללת עסקה | לפני עסקה בבלוק |
| זיהוי תבניות | רק נתונים על-השרשרת | Mempool, חוצה-פרוטוקול |
| תוצאות שווא | נמוכות (ספים מדויקים) | גבוהות יותר (ניתוח רעש) |
| אינטגרציה | חוזה חכם | Node.js, The Graph, Grafana |
אופטימלי: שילוב שתי הגישות.
מה כלול בעבודה
- ניתוח ארכיטקטורת פרוטוקול וסקירת נתונים היסטוריים (TVL, משיכות, הזנות אורקל)
- תכנון טריגרים וספים עם ניתוח P95/P99
- פיתוח חוזה חכם ב-Solidity (OpenZeppelin, Foundry)
- הקמת ניטור מחוץ-לשרשרת
- פריסת ארנק multisig למועצת ביטחון
- ביקורת קוד ואימות פורמלי
- תיעוד והדרכת צוות
- שנה של תמיכה לאחר השקה
דוגמת תצורה
עבור פרוטוקול הלוואות עם TVL של 50 מיליון דולר, ספים אופייניים:
- משיכה מקסימלית לשעה: 15% TVL
- יציאה נטו מקסימלית: 20% TVL
- סטיית אורקל: 5% מ-TWAP
- מגבלת ניצול: 95% פרמטרים אלה נבדקים עם כל עדכון.
שלבי העבודה
- אנליטיקה — איסוף מדדים, ראיונות צוות, זיהוי סיכונים
- עיצוב — בחירת רמות, כוונון פרמטרים, ארכיטקטורת מועצת ביטחון
- יישום — כתיבת חוזה חכם, אינטגרציית אורקל
- בדיקות — יחידה, אינטגרציה, בדיקות fuzz (Foundry, Echidna)
- ביקורת — ביקורת חיצונית עם אימות פורמלי
- פריסה — פריסה על L1/L2 נבחר, הקמת ניטור
לוחות זמנים ועלות
המחזור המלא למערכת ברמת ייצור: 4–6 חודשים. העלות תלויה במורכבות הפרוטוקול ובמספר הטריגרים. הלקוחות שלנו חוסכים בדרך כלל עשרות מונים יותר בהתקפות שנמנעו. ארכיטקטורה חזקה מחזירה את עצמה במהירות. צרו קשר — נעריך את הפרויקט שלכם תוך 2–3 ימי עסקים.
טעויות יישום נפוצות
- ספים נוקשים ללא ניתוח היסטורי — מוביל לתוצאות שווא וחסימת פעולות לגיטימיות.
- סמכות עצירה לאדם יחיד — יוצר סיכון ריכוזיות ופגיעות להתקפות ממשל.
- התעלמות ממשיכות גדולות לגיטימיות — פתרון: רשימת כתובות מורשות או משיכה דו-שלבית.
- איפוס מהיר מדי של ההשהיה — ה-timelock לאיפוס צריך להיות ארוך מהרגיל (7+ ימים).
בקשו ייעוץ — המהנדסים שלנו עם חמש שנות ניסיון ב-DeFi יעזרו לאבטח את הפרוטוקול שלכם. קבלו הצעה מסחרית.







