אנו מפתחים מערכות הצבעה on-chain עבור DAOs בכל קנה מידה—מקהילות קטנות ועד פרוטוקולים עם שווי שוק של מיליארדי דולרים. הטכנולוגיה שלנו: Solidity 0.8.x, Foundry, OpenZeppelin Governor. אתם מקבלים מערכת ממשל אמינה: טוקן עם הצבעה מבוססת snapshot, Timelock, הגנה מפני התקפות flash loan, והתאמה אישית למודל הניהול שלכם.
הבעיה העיקרית של רוב ה-DAOs היא או עיצוב מרכזי מדי (הכל נקבע על ידי multisig) או לא תפקודי (quorum אף פעם לא מושג). אנו מוצאים את האיזון: אנו מכוונים את עיכוב ההצבעה, ה-quorum וה-timelock בהתבסס על ניתוח הקהילה שלכם. 10+ שנות ניסיון בפיתוח blockchain, 30+ פרויקטי DAO שיושמו—אנו מבטיחים תוצאה עובדת. לפי DefiLlama, יותר מ-50% מה-DAOs חסרים Timelock מגן, דבר קריטי לאבטחה.
OpenZeppelin Governor גמיש פי 4 מ-Compound Governor Bravo בזכות mixins מודולריים, ו-Foundry לבדיקות מאיץ את מחזור הפיתוח פי 3 בהשוואה ל-Hardhat. אתם חוסכים עד 40% על ביקורות (audits) בזכות בדיקות פורמליות מובנות בשלב הבדיקות.
אילו בעיות אנו פותרים?
Quorum לא אופטימלי—נמוך מדי מאפשר להצעות זדוניות לעבור, גבוה מדי משתק את הממשל. אנו מנתחים את שיעור ההשתתפות האמיתי ומכיילים את ה-quorum ידנית. התקפות flash loan על הממשל—תוקף לוקח flash loan, מקבל כוח הצבעה עצום, מעביר הצעה, ומרוקן את הקופה. ההגנה שלנו: עיכוב הצבעה + הצבעה מבוססת snapshot. ריכוזיות דרך multisig—אם כל ההחלטות המרכזיות עוברות דרך multisig 3/5, זה לא DAO. אנו בונים ממשל fully on-chain עם ביזור הדרגתי. אובדן כספים עקב שגיאות Timelock—השארת תפקיד admin ב-TimelockController היא גורם להאקרים רבים. אנו מבטלים אוטומטית את כל תפקידי ה-admin לאחר הפריסה.
ארכיטקטורה: טוקן, Governor ו-Timelock
מערכת החוזים הבסיסית ל-DAO—Governance Token (ERC-20 עם Votes), Governor (ליבת ההצבעה), ו-TimelockController (עיכוב מגן).
איך להקים OpenZeppelin Governor?
הרכבה מינימלית דרך ירושה של mixins:
// 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 MyDAO is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl {
constructor(
IVotes _token,
TimelockController _timelock
)
Governor("MyDAO")
GovernorSettings(
1 days, // voting delay
1 weeks, // voting period
100_000e18 // proposal threshold
)
GovernorVotes(_token)
GovernorVotesQuorumFraction(4) // 4% quorum
GovernorTimelockControl(_timelock)
{}
// Overrides обязательны для разрешения конфликтов миксинов
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 _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) internal override(Governor, GovernorTimelockControl) {
super._execute(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();
}
function supportsInterface(bytes4 interfaceId) public view override(Governor, GovernorTimelockControl) returns (bool) {
return super.supportsInterface(interfaceId);
}
} למה Timelock כל כך קריטי?
TimelockController הוא העיכוב בין קבלת ההצעה לביצועה. בלעדיו, תוקף שמשיג שליטה על ההצבעה יכול לרוקן את כל הקופה באופן מיידי.
// Деплой TimelockController
TimelockController timelock = new TimelockController(
2 days, // minDelay
proposers, // кто может ставить в очередь (Governor)
executors, // кто может выполнять (address(0) = anyone)
admin // admin (обычно address(0) после setup)
);
// Назначить роли
timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor));
timelock.grantRole(timelock.CANCELLER_ROLE(), address(governor));
timelock.grantRole(timelock.EXECUTOR_ROLE(), address(0));
// Критично: отозвать admin у deployer!
timelock.revokeRole(timelock.TIMELOCK_ADMIN_ROLE(), deployer);השלב האחרון לעיתים קרובות מושמט—וכתוצאה מכך ה-deployer יכול לעקוף את הממשל. אנו תמיד בודקים זאת.
טוקן ממשל עם ERC-20 Votes
טוקן ההצבעה חייב ליישם את ממשק // 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 MyDAO is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { constructor( IVotes _token, TimelockController _timelock ) Governor("MyDAO") GovernorSettings( 1 days, // voting delay 1 weeks, // voting period 100_000e18 // proposal threshold ) GovernorVotes(_token) GovernorVotesQuorumFraction(4) // 4% quorum GovernorTimelockControl(_timelock) {} // Overrides обязательны для разрешения конфликтов миксинов 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 _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) internal override(Governor, GovernorTimelockControl) { super._execute(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(); } function supportsInterface(bytes4 interfaceId) public view override(Governor, GovernorTimelockControl) returns (bool) { return super.supportsInterface(interfaceId); } } . OpenZeppelin ERC20Votes מאחסן היסטוריית checkpoints של יתרות לצורך הצבעה מבוססת snapshot.
contract GovernanceToken is ERC20, ERC20Permit, ERC20Votes {
constructor(address initialHolder) ERC20("MyDAO Token", "MDT") ERC20Permit("MyDAO Token") {
_mint(initialHolder, 10_000_000e18);
}
function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
super._afterTokenTransfer(from, to, amount);
}
function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
super._mint(to, amount);
}
function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
super._burn(account, amount);
}
}נקודה חשובה: ב-ERC20Votes, לטוקנים אין כוח הצבעה עד שהבעלים קורא ל-// Деплой TimelockController TimelockController timelock = new TimelockController( 2 days, // minDelay proposers, // кто может ставить в очередь (Governor) executors, // кто может выполнять (address(0) = anyone) admin // admin (обычно address(0) после setup) ); // Назначить роли timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor)); timelock.grantRole(timelock.CANCELLER_ROLE(), address(governor)); timelock.grantRole(timelock.EXECUTOR_ROLE(), address(0)); // Критично: отозвать admin у deployer! timelock.revokeRole(timelock.TIMELOCK_ADMIN_ROLE(), deployer); . אנו הופכים self-delegation לאוטומטי בהעברה הראשונה כדי למנוע בלבול.
מחזור החיים של הצעה והצבעה עם נימוק
הצעה עוברת שלבים: Pending → Active → Succeeded/Defeated → Queued → Executed (או Canceled). כל הצבעה יכולה להיות מלווה בנימוק—זה מגביר את השקיפות. אנו מיישמים הצבעה היברידית: הצבעה ללא גז באמצעות חתימות EIP-712 (איסוף הצבעות off-chain עם סיום on-chain) והצבעה on-chain מסורתית. ה-relayer משלם את הגז, המשתמש חותם על ההצבעה off-chain—מוריד את חסמי ההשתתפות.
ניהול קופה והגנה מפני התקפות flash loan
חוזה הקופה נשלט על ידי ה-Governor דרך Timelock. בנוסף, מוגדר multisig של Guardian (לדוגמה, 5/9) להקפאת חירום—הוא יכול רק להקפיא כספים, לא לבזבז אותם.
הגנה מפני flash loan: עיכוב הצבעה (מינימום יום אחד) מונע מתוקף להצביע באופן מיידי. ה-snapshot קובע את היתרות בבלוק של יצירת ההצעה, לא בזמן ההצבעה.
מנגנונים מותאמים אישית ושדרוג
אנו מוסיפים Quadratic voting (כוח הצבעה = sqrt(balance)) כדי להפחית את השפעת ה"לווייתנים" ו-Conviction voting למימון רציף. חוזי Governor נפרסים דרך UUPS proxy—שדרוגים עוברים מחזור ממשל מלא דרך self-call.
function _getVotes(
address account,
uint256 blockNumber,
bytes memory /*params*/
) internal view virtual override returns (uint256) {
uint256 balance = token.getPastVotes(account, blockNumber);
return _sqrt(balance);
}
function _sqrt(uint256 x) internal pure returns (uint256 y) {
if (x == 0) return 0;
uint256 z = (x + 1) / 2;
y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
} contract UpgradeableGovernor is Governor, UUPSUpgradeable {
function _authorizeUpgrade(address newImplementation) internal override onlyGovernance {}
modifier onlyGovernance() {
require(msg.sender == address(this), "Only governance can upgrade");
_;
}
}במהלך שדרוג UUPS, הלוגיקה מתעדכנת ב-implementation בעוד שהאחסון נשאר ב-proxy. כל השינויים עוברים דרך מנגנון ההצבעה: נוצרת הצעה עם קריאה ל-IVotes, הצבעה, Timelock, ולאחר מכן ביצוע. זה מבטיח ניהול שדרוג מבוזר.
טעויות נפוצות ופרמטרים מומלצים
- Timelock קצר. 24 שעות זה מעט מדי עבור DeFi. הגדירו 48–72 שעות, עבור שדרוגים גדולים—7 ימים.
- Quorum נמוך. 4% הוא נורמלי עבור פרוטוקולים גדולים, אבל עבור קהילה קטנה הפעילות האמיתית עשויה להיות נמוכה יותר. כיילו לאחר ההשקה.
- חסר סף הצעות. ללא סף, כל אחד יכול להציף בהצעות. הגדירו סף = 0.5–1% מההיצע הכולל.
- תפקיד admin שנשאר. תמיד בטלו את TIMELOCK_ADMIN_ROLE מה-deployer.
| פרמטר | DAO קטן | פרוטוקול DeFi | DAO של קופה |
|---|---|---|---|
| עיכוב הצבעה | יום אחד | יומיים | יום אחד |
| תקופת הצבעה | 5 ימים | 7 ימים | 7 ימים |
| Timelock | 24 שעות | 72 שעות | 48 שעות |
| Quorum | 10% | 4% | 5% |
| סף הצעות | 1% | 0.25% | 0.5% |
| מנגנון | OpenZeppelin Governor | Compound Governor Bravo |
|---|---|---|
| מודולריות | כן (mixins) | לא (לוגיקה קבועה) |
| Timelock | תמיכה מובנית | דורש חוזה חיצוני |
| שדרוג | דרך UUPS | דרך delegatecall |
| יעילות גז | גבוהה יותר (אחסון אופטימלי) | נמוכה יותר |
מה כלול ולוחות זמנים
אנו מספקים:
- עיצוב מנגנוני הצבעה (בחירת מודל הצבעה, quorum, timelock)
- חוזים חכמים (טוקן ERC-20 עם Votes, Governor, Timelock, קופה)
- מערכת בדיקות מלאה (בדיקות mainnet fork, סימולציות התקפה)
- פריסה והגדרה (כולל ביטול תפקידי admin)
- תיעוד והדרכת צוות
- תמיכה ל-3 החודשים הראשונים לאחר ההשקה
לוחות זמנים—מ-4 שבועות למערכת בסיסית ועד 16 שבועות עם התאמות אישיות ופרונטאנד. העלות משתנה, אבל בממוצע נמוכה ב-30% מחלופות עם פונקציונליות דומה. צרו קשר כדי לדון בפרויקט שלכם ולקבל הערכה ראשונית. אנו מבטיחים שקיפות בכל השלבים—מעיצוב ועד פריסה. בקשו ייעוץ חינם.







