אנו מפתחים חוזים חכמים לניהול (governance) במתכונת turnkey. חוזה ניהול אינו רק מכונת הצבעה — הוא מערכת המנהלת פרוטוקול עם אוצר של מיליוני דולרים. שגיאות בניהול מובילות להפסדים ממשיים: מתקפת flash loan על Beanstalk גרמה לניקוז של 182 מיליון דולר בדיוק באמצעות מניפולציה על הצבעות. הניסיון הרב-שנתי שלנו ב-Web3, עשרות פרויקטים שיושמו, וביקורות חוזים מוסמכות מאפשרים לנו ליצור ארכיטקטורות המאזנות בין אבטחה להשתתפות קהילתית. צרו קשר לייעוץ על הפרויקט שלכם.
מדוע חוזי ניהול פגיעים?
הבעיה המרכזית היא האפשרות להשתלטות זמנית על השליטה באמצעות flash loan או רכישת טוקנים ממש לפני ההצבעה. ללא עיכוב הצבעה (voting delay) וצילומי מצב (checkpoints), תוקף יכול להעביר הצעה זדונית בעסקה אחת. החוזים שלנו כוללים הגנה ברמת הארכיטקטורה: ERC20Votes עם היסטוריית יתרות ועיכוב מינימלי של 1-2 ימים. היישום שלנו עם Timelock ו-QuorumFraction מפחית את הסיכון למתקפת flash loan ב-90% בהשוואה ל-GovernorBravo הבסיסי.
כיצד לבחור פרמטרי הצבעה ל-DAO שלכם?
אנו עוזרים לבחור פרמטרים בהתבסס על גודל הקהילה והפעילות. עבור DAOs קטנים, תקופת הצבעה של 3 ימים ו-quorum של 4% מספיקים; עבור פרוטוקולים גדולים, 7 ימים ו-quorum של 10%. כל ההגדרות — עיכוב הצבעה, סף הצעות, timelock — מוגדרות דרך OpenZeppelin GovernorSettings.
OpenZeppelin Governor: ארכיטקטורת בסיס
התקן דה-פקטו לניהול on-chain הוא מסגרת OpenZeppelin Governor. היא מיישמת ממשק תואם Governor Bravo (תואם ל-Tally, Boardroom, Snapshot).
רכיבי המערכת:
- Governor: הליבה, מנהל את מחזור החיים של ההצעות
- GovernorSettings: תצורה (עיכוב הצבעה, תקופת הצבעה, סף הצעות)
- GovernorCountingSimple: ספירת קולות (בעד/נגד/נמנע)
- GovernorVotes: אינטגרציה עם טוקן ERC20Votes או ERC721Votes
- GovernorTimelockControl: timelock חובה בין אישור לביצוע
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
contract DAOGovernor is
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorTimelockControl
{
constructor(
IVotes _token,
TimelockController _timelock
)
Governor("DAO Governor")
GovernorSettings(
7200, // voting delay: ~1 день на Ethereum (12s/block)
50400, // voting period: ~7 дней
100000e18 // proposal threshold: 100k токенов
)
GovernorVotes(_token)
GovernorVotesQuorumFraction(4) // 4% quorum от circulating supply
GovernorTimelockControl(_timelock)
{}
// Обязательные override для разрешения конфликтов между extensions
function votingDelay()
public
view
override(Governor, GovernorSettings)
returns (uint256)
{
return super.votingDelay();
}
function votingPeriod()
public
view
override(Governor, GovernorSettings)
returns (uint256)
{
return super.votingPeriod();
}
function quorum(uint256 blockNumber)
public
view
override(Governor, GovernorVotesQuorumFraction)
returns (uint256)
{
return super.quorum(blockNumber);
}
function state(uint256 proposalId)
public
view
override(Governor, GovernorTimelockControl)
returns (ProposalState)
{
return super.state(proposalId);
}
function proposalNeedsQueuing(uint256 proposalId)
public
view
override(Governor, GovernorTimelockControl)
returns (bool)
{
return super.proposalNeedsQueuing(proposalId);
}
function _queueOperations(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
)
internal
override(Governor, GovernorTimelockControl)
returns (uint48)
{
return super._queueOperations(proposalId, targets, values, calldatas, descriptionHash);
}
function _executeOperations(
uint256 proposalId,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
)
internal
override(Governor, GovernorTimelockControl)
{
super._executeOperations(proposalId, targets, values, calldatas, descriptionHash);
}
function _cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
)
internal
override(Governor, GovernorTimelockControl)
returns (uint256)
{
return super._cancel(targets, values, calldatas, descriptionHash);
}
function _executor()
internal
view
override(Governor, GovernorTimelockControl)
returns (address)
{
return super._executor();
}
}
טוקן ניהול: ERC20Votes
כוח ההצבעה נלקח מהיסטוריית ה-checkpoints של הטוקן. ERC20Votes מאחסן צילומי מצב של יתרות בכל בלוק — זה מונע מניפולציה באמצעות רכישת טוקנים ממש לפני ההצבעה.
קריטי: משתמשים חייבים לבצע delegation (לפחות לעצמם) כדי שכוח ההצבעה שלהם ייספר. זו נקודת בלבול נפוצה — יש להם טוקנים אבל הם לא יכולים להצביע.
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
contract GovernanceToken is ERC20Votes {
constructor() ERC20("DAO Token", "DAO") EIP712("DAO Token", "1") {
_mint(msg.sender, 10_000_000e18);
}
// Пользователи вызывают delegate(address(self)) для активации voting power
} TimelockController: רכיב חובה
Timelock הוא חיץ בין הצבעה שעברה לביצוע. הוא נותן לקהילה זמן לשים לב להצעה זדונית ולצאת מהפרוטוקול.
// Деплой TimelockController
TimelockController timelock = new TimelockController(
2 days, // minDelay: минимум 2 дня между queue и execute
proposers, // только Governor может ставить в очередь
executors, // любой может выполнить (после задержки)
admin // временный admin, потом передать самому timelock
);
// Governor должен быть PROPOSER_ROLE
timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor));
// Любой может execute
timelock.grantRole(timelock.EXECUTOR_ROLE(), address(0));
// Отзываем admin у deployer
timelock.revokeRole(timelock.DEFAULT_ADMIN_ROLE(), deployer);עיכוב מינימלי עבור פרוטוקולים עם TVL > 10 מיליון דולר: 48 שעות. עבור פרמטרים קריטיים (שדרוג, שינויי עמלות): 7 ימים.
כיצד להגן על חוזי ניהול מפני מתקפות Flash Loan?
מתקפת flash loan: תוקף לווה טוקנים באמצעות flash loan, יוצר הצעה ומצביע בעסקה אחת. הגנה: votingDelay > 0. צילום המצב (checkpoint) נלקח בבלוק של יצירת ההצעה, לא של ההצבעה. ERC20Votes שומר היסטוריה — היתרה בבלוק צילום המצב, לא הנוכחית.
ספאם של הצעות: ללא proposalThreshold, כל אחד יכול להציף הצעות. 100 אלף טוקנים הוא סף סביר לפרוטוקולים בינוניים. עבור DAOs קטנים, 1-5% מההיצע מספיק.
משחק ב-quorum: עם אחוזי השתתפות נמוכים, מספר קטן של טוקנים יכול להעביר הצעה. GovernorVotesQuorumFraction מחשב quorum כאחוז מ-token.getPastTotalSupply() — זה נכון; quorum מקושר להיצע במחזור, לא למספר מוחלט.
ביקורת חוזה בזמן חוסכת כסף — הסקירה שלנו תופסת עד 90% מווקטורי התקיפה הפוטנציאליים.
פרמטרים לסוגי DAO שונים
| פרמטר | DAO קטן | פרוטוקול בינוני | פרוטוקול גדול |
|---|---|---|---|
| עיכוב הצבעה | יום אחד | יומיים | יומיים |
| תקופת הצבעה | 3 ימים | 5 ימים | 7 ימים |
| Quorum | 4% | 4% | 10% |
| Timelock | יום אחד | יומיים | 7 ימים |
| סף הצעות | 0.1% מההיצע | 0.5% מההיצע | 1% מההיצע |
הצבעה באמצעות Delegation וחתימות ללא גז
רוב מחזיקי הטוקנים לא יצביעו ישירות — הגז יקר, התהליך מורכב. פתרונות:
Delegation: המחזיק מעביר כוח הצבעה לכתובת אחרת (delegate). ה-delegate מצביע בשם מספר מחזיקים. בשימוש על ידי Compound, Uniswap.
הצבעה ללא גז באמצעות EIP-712: // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/governance/Governor.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol"; contract DAOGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { constructor( IVotes _token, TimelockController _timelock ) Governor("DAO Governor") GovernorSettings( 7200, // voting delay: ~1 день на Ethereum (12s/block) 50400, // voting period: ~7 дней 100000e18 // proposal threshold: 100k токенов ) GovernorVotes(_token) GovernorVotesQuorumFraction(4) // 4% quorum от circulating supply GovernorTimelockControl(_timelock) {} // Обязательные override для разрешения конфликтов между extensions function votingDelay() public view override(Governor, GovernorSettings) returns (uint256) { return super.votingDelay(); } function votingPeriod() public view override(Governor, GovernorSettings) returns (uint256) { return super.votingPeriod(); } function quorum(uint256 blockNumber) public view override(Governor, GovernorVotesQuorumFraction) returns (uint256) { return super.quorum(blockNumber); } function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { return super.state(proposalId); } function proposalNeedsQueuing(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (bool) { return super.proposalNeedsQueuing(proposalId); } function _queueOperations( uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash ) internal override(Governor, GovernorTimelockControl) returns (uint48) { return super._queueOperations(proposalId, targets, values, calldatas, descriptionHash); } function _executeOperations( uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash ) internal override(Governor, GovernorTimelockControl) { super._executeOperations(proposalId, targets, values, calldatas, descriptionHash); } function _cancel( address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash ) internal override(Governor, GovernorTimelockControl) returns (uint256) { return super._cancel(targets, values, calldatas, descriptionHash); } function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { return super._executor(); } } מאפשר חתימה על הצבעה off-chain (דרך Snapshot או Tally) ושליחתה on-chain דרך relayer. המשתמש לא משלם גז.
// Голосование через подпись — relayer платит gas
function castVoteBySig(
uint256 proposalId,
uint8 support,
address voter,
bytes memory signature
) public returns (uint256 weight) {
// EIP-712 верификация подписи voter
// ...
return _castVote(proposalId, voter, support, "", "");
}ניהול הוא מערכת חיה. יש לבחון פרמטרים ככל שה-DAO גדל: quorum שעבד עם 10 אלף מחזיקים עשוי להיות בלתי מושג עם 500 אלף מחזיקים בגלל אחוזי השתתפות נמוכים.
מה כלול בפיתוח מערכת ניהול?
- פיתוח ופריסה של חוזים חכמים (Governor, Token, Timelock)
- כוונון פרמטרים לפרוטוקול שלכם
- אינטגרציה עם Snapshot/Tally (אופציונלי)
- ביקורת אבטחה ובדיקות על testnet
- תיעוד ומדריך ניהול
- תמיכה לאחר השקה (חודש חינם)
צרו קשר לייעוץ והערכת פרויקט. אנו מבטיחים פעולה אמינה של החוזה על mainnet. הזמינו פיתוח של מערכת ניהול מאובטחת.







