一個聽程式碼、而不聽老闆的金庫
2021 年 11 月,一群素未謀面的網路陌生人,在不到一週內湊出了大約 11,600 顆 ETH——約 4,700 萬美元,想在蘇富比拍賣會上買下美國憲法現存少數幾份原始印本之一。他們自稱 ConstitutionDAO。最後他們輸給了 Citadel 的 Ken Griffin(落槌價 4,320 萬美元),事後代幣持有者可以取回自己那一份。真正令人驚訝的不是這場失利,而是那台「機器」:一萬七千個從未見過面的人,竟組起了一筆數千萬美元的軍費,沒有公司、沒有銀行帳戶,也沒有任何單一個人能捲款走人。鑰匙是由程式碼握著的。
DAO 是 Decentralized Autonomous Organization(去中心化自治組織) 的縮寫,但這個詞造成的困惑往往多過它的說明力。所以把它拆開來看。一間傳統公司,說到底就是一個銀行帳戶,加上一本規則書(公司章程),再加上銀行信任、能簽字的人。一個 DAO 保留了前兩樣,刪掉了第三樣。它的錢放在一個 金庫 裡,它的規則寫成 智慧合約,而取代受信任簽字者的,是規則本身決定金錢何時移動。沒有財務長、沒有匯款核可、沒有分行經理——只有在條件滿足時、也唯有在條件滿足時才會執行的程式碼。
兩種握鑰匙的方式:多簽 對上 代幣投票
每個 DAO 在做任何事之前,都得先回答一個問題:究竟誰有權核准動用金庫裡的錢? 主流答案有兩種,而大多數真實組織,都落在這兩者之間的某一點上。
第一種答案是 多簽錢包——一份只在 N 位指定持有人中的 M 位簽署後才會放款的合約,例如 3-of-5 的 Gnosis Safe。它簡單、快速、久經沙場;ConstitutionDAO 實際上正是這樣保管它的 ETH 的。代價是:你必須信任那些被指名的人類。如果 5 人中有 3 人串通(或被釣魚),金庫就是他們的了。多簽把單一故障點分散成了少數幾個故障點,這是實實在在的進步——但它是一個由受信任的人組成的聯邦,還不是一個無需信任的組織。
// A 3-of-5 multisig "Safe": funds move only with enough valid approvals
function executeTransaction(address to, uint256 value, bytes data, Signature[] sigs) external {
require(sigs.length >= threshold, "not enough approvals"); // threshold = 3
address last = address(0);
for (uint i = 0; i < threshold; i++) {
bytes32 h = hashTx(to, value, data, nonce); // what each owner signed
address owner = recover(h, sigs[i]); // ECDSA: signature -> address
require(isOwner[owner] && owner > last, "bad or duplicate signer");
last = owner; // strictly increasing => no double-count
}
nonce++; // burn the nonce: no replay
(bool ok, ) = to.call{value: value}(data); // the Safe itself makes the call
require(ok, "inner call failed");
}第二種答案是完整的 鏈上治理:權力分散到成千上萬名 治理代幣 持有者手中,而投票的結果由合約自動執行,完全沒有人類簽字。這就是 代幣加權投票——一枚代幣,一票——也是人們口中「真正的」DAO 通常所指的東西。它能擴展到匿名的陌生人、移除了受信任的委員會,但它也較慢、可能被巨鯨壟斷,並且需要多得多的機械裝置。本階的下一篇會詳細解剖那套投票機制;在這裡,我們只需先看清它落在哪個位置。
金庫:DAO 真正掌控的那一樣東西
把一個 DAO 拆到只剩承重牆,你會找到那個 金庫:一堆由合約(而非個人)持有的資產——ETH、穩定幣、DAO 自己的代幣,有時還有 NFT。這些可不是玩具規模。成熟協定的金庫曾持有相當於數十億美元的價值(Uniswap 與 Optimism 都在其列)。它最關鍵的性質殘酷地簡單:沒有任何一把私鑰能把金庫掃空。 它的擁有者是一份合約,而通往那份合約的唯一一扇門,就是一個治理決策。
在最常見的正式架構中(OpenZeppelin 的 Governor + Timelock),資產甚至不和投票合約放在一起。它們存放在一個獨立的 TimelockController(時間鎖控制器) 之後,而那才是資金真正的持有者與管理員。投票合約只能請求時間鎖去發出呼叫;時間鎖則在一段強制延遲之後才執行。這樣接線意味著:投票計數邏輯裡的單一漏洞,無法瞬間掏空金庫——中間永遠有一間等候室。
// Who owns what, in a typical on-chain DAO (OpenZeppelin stack): // // GovernanceToken -> mints "weight": balance at a snapshot block = your voting power // Governor -> tallies votes; can ONLY propose & queue calls into the Timelock // TimelockController -> HOLDS the treasury (ETH, USDC, NFTs) and executes the calls // after a mandatory delay; its admin is the Governor, not a human // // So the only path to the money is: // token holders vote -> Governor approves -> Timelock waits -> Timelock executes // There is deliberately no shortcut.
提案的生命週期:從一個念頭到鏈上執行
接下來這部分,會讓你第一次親眼看見時覺得像魔法。一份 治理提案 不是一段表達意圖的文字——它是這個 DAO 在投票通過後將會執行的、確切的那一筆(或幾筆)交易,事先就被編碼好了。「撥 50,000 USDC 給文件團隊」不是一句承諾;它就是一個字面上的呼叫 `USDC.transfer(grantee, 50000e6)`,合約會替你發出。那段英文說明,只不過是釘在位元組碼上的一張標籤。
// OpenZeppelin Governor: a proposal IS the list of calls to make if it passes
address[] memory targets = new address[](1);
uint256[] memory values = new uint256[](1);
bytes[] memory calldatas = new bytes[](1);
targets[0] = USDC; // the contract the DAO will call
values[0] = 0; // no ETH attached to this call
calldatas[0] = abi.encodeCall( // exactly: transfer 50,000 USDC to the grantee
IERC20.transfer, (grantee, 50_000e6) // USDC has 6 decimals => 50_000e6
);
uint256 proposalId = governor.propose(
targets, values, calldatas,
"GP-42: Fund the docs team with 50,000 USDC from the treasury"
);
// proposalId == keccak256(abi.encode(
// targets, values, calldatas, keccak256(bytes(description))
// )); -> the id is a hash of the calls, so the description can never be swapped out從這裡起,提案會走過一串固定的鏈上狀態。每一次狀態轉換都由合約強制執行,因此沒有人能跳過任一步、在截止後補投,或執行一個落選的東西。
- 提案(Propose)。 一位投票權重高於提案門檻的持有者,提交那組已編碼的呼叫加上一段說明。合約會記下這份提案與一個快照區塊。設定門檻,是為了避免任何只持有一枚代幣的人就把佇列灌爆。
- 投票延遲 → 進行中(Active)。 經過一段短暫延遲(讓人有時間準備、也讓快照鎖定)後,投票開放。持有者投下贊成/反對/棄權,權重依其在快照區塊當下的代幣餘額計算——快照之後才買進的代幣,在這份提案上換不到任何票。
- 計票 → 通過或否決。 投票期結束時,合約會檢查兩件事:贊成是否多過反對,以及是否達到 法定門檻(投出的票數至少要到某個下限)?一份提案可能贏了票數,卻仍因法定人數不足而失敗——這是一道防線,防止一小撮不具代表性的小圈子替所有人決定。
- 排入佇列(時間鎖)。 通過的提案會被排入 時間鎖,啟動一段強制等待期——常見是 2 天。此時什麼都還沒移動。這段延遲是整個系統裡最重要的安全閥,所以它值得自成一步。
- 執行(Execute)。 一旦時間鎖延遲走完,任何人都能呼叫 `execute()`——時間鎖便發出當初提案的那幾筆確切呼叫。50,000 USDC 離開金庫。這個組織剛剛行動了,而沒有任何一個人簽署那筆轉帳。是鏈做了這件事,因為規則這麼說。
為什麼時間鎖這麼重要?因為它給了在投票中落敗的少數方反應的時間。如果一份即將通過的提案會把控制權交給攻擊者,或改掉一條你無法接受的規則,這段延遲就是你的窗口——讓你在變更生效前把資金提走、退出部位,或集結反對力量。一個沒有時間鎖的 DAO,距離瞬間且不可逆的損害,只差一次糟糕的投票——這正是治理攻擊那一篇會火力全開重提的主題。
是鏈上治理,還是只是個品牌?Snapshot 之問
現在來談誠實的那一面。上面那條乾淨的「提案—投票—排隊—執行」流水線,是具約束力的鏈上治理:投票與行動是同一條交易軌跡,並由程式碼執行。但有很大一部分自稱 DAO 的組織,根本不是這樣運作的。
實務上最常見的模式是鏈下信號(off-chain signaling)。持有者在 Snapshot 上投票——那是一個免費、免 gas 的平台,票是儲存在鏈下的簽署訊息,並依某個過去區塊讀到的代幣餘額來加權。它便宜又有包容性——但一場 Snapshot 投票什麼都不會執行。它是一場民調。要把結果化為行動,仍得由一個受信任的 多簽 團隊親手去送出那筆交易。這完全合情合理,也是多數專案的做法——但要看得清楚:這是 鏈下治理 外加一個人類委員會。社群發出信號;一小群人決定要不要遵守這個信號。
所以,判斷某樣東西是不是真正的 DAO,標準不在於它的 logo 或 Discord——而在於:一份通過的投票,是會自己把錢搬動,還是仍得有個人類選擇去服從它? 多數專案落在一條光譜上:一端是純多簽,中間是「鏈下 Snapshot 餵給多簽」,另一端則是完全具約束力的「鏈上 Governor + 時間鎖」。委託投票(讓被動的持有者把自己的權重指派給活躍、可問責的代表)正是讓具約束力那一端在規模化下仍可運作的關鍵,因為大多數代幣持有者根本從不投票。
侷限:速度、金權政治,與法律的缺口
若就停在「魔法」這裡作結,是不誠實的。鏈上組織帶著真實而結構性的弱點,而本階接下來的篇章,正是為了它們而存在。現在有三點值得先點名。
它們又慢又僵。 一個新創 CEO 一個下午就能上線的變更,DAO 可能得花上一週:提案門檻、投票延遲、投票期、時間鎖。這段延遲,是「沒有老闆」的代價——而在瞬息萬變的危機中,它有時是致命的。它們傾向金權政治。 一枚代幣一票,意味著一塊錢一票;誰持有最多代幣,誰就握有最大權力,而冷漠會讓權力更加集中,因為小持有者乾脆不出門投票。更糟的是,投票權重可以被租借:攻擊者能在單一區塊內借進一座代幣大山,硬把一份惡意 提案 推過去——這正是後面幾篇要解剖的 治理攻擊,也是時間鎖之所以存在的一大理由。
還有一道法律的缺口。 「程式即法律(Code is law)」是一句口號,不是法院判決。一個 DAO 可以毫無瑕疵地持有資金、執行投票,卻沒有清楚的法律人格——這就引出了關於責任歸屬、稅務,以及與外部世界簽約的棘手問題。一些司法管轄區已開始回應(懷俄明州的 DAO LLC、馬紹爾群島的 DAO 法案),讓 DAO 能套上一層 法律外殼,好讓它能簽下租約、或限制成員的責任。鏈上規則與鏈下法律能否調和,至今仍是一個開放、且持續演進中的問題。
請牢牢記住這一切底下的那一個核心觀念:一個 DAO,就是一份握著 金庫 鑰匙的 智慧合約,外加一套決定「這些鑰匙何時轉動」的流程。本階接下來的一切,都是這套流程的細節——票如何計算與委託、攻擊者如何試圖顛覆它、如何設計能抵抗財富與女巫攻擊的選票,以及一個代幣的經濟學如何讓整個系統保持誠實、而不是長成龐氏的形狀。