Multi-sig wallet contracts 是一種有多個擁有者的合約,當其中一個擁有者想要執行某個指令時,就需要其他擁有者的簽核同意,當同意門檻達到時這個指令才會執行。用途主要是「避免單點錯誤」,如果只有一把私鑰掌控整筆資金,那被駭的可能就非常高。也就是把資金操控權切割達到雞蛋不要放在同一個籃子裡的作用。如果一定比例以下的私鑰遭駭,甚至有辦法利用共識機制取消被駭私鑰的資金存取權。
Multisig wallets 在比特幣中很早就出現了,許多組織和公司都很喜歡使用管理資金的方式。有許多種 Multisig Contract:
這邊我覺得 Solidity by Example - Multi-Sig Wallet 的例子很方便初學者理解什麼是多簽錢包。這個範例裡面主要有幾個重點資料結構:
address[] public owners;
:誰是這個多簽錢包的擁有者struct Transaction
:一個交易物件該有什麼元素mapping(uint => mapping(address => bool)) public isConfirmed
:每一筆交易的狀態Transaction[] public transactions
:儲存所有交易在這個多簽錢包合約執行某筆交易之前必須有三個步驟:
submitTransaction
:會把交易資訊建置並且 push 到 transactions
中待核准。confirmTransaction
:owner
可以把 transactions
中待核准的交易進行核准executeTransaction
:如果此筆交易已經核准,就會執行這筆交易而多簽錢包的重點就在於以下這段在 executeTransaction
中的程式碼:
require(
transaction.numConfirmations >= numConfirmationsRequired,
"cannot execute tx"
);
也就是這筆交易的核准者數量必須大於我們一開始設定的門檻數:numConfirmationsRequired
。
接下來我們接續著可升級合約的內容,來看看以下這個 Openzeppelin 提供的例子。
可升級合約理論上只有一個 EOA 來決定現在的行為,然而「升級」這個動作需要多個擁有者來同意是一個聽起來很合理的事情,畢竟如果今天一個產品說換 code 就換 code,那產品的客戶們也會覺得很沒有保障吧。系統設計上,通常產品會有一個 DAO 來支援這個動作,也就是股民們(廣義來說)有資格去決定 DAO 支不支持這個決議,如果這個決議通過才有辦法進行升級。
創建這一個多簽可升級合約的過程為:
如何建置一個可升級合約我們在之前有提過,未來也會提到不同的 Pattern,最重要的部分在於我們要如何在合約中判斷 owner 是誰(也就是 ProxyAdmin 是誰),以及如何使用 multisig 來決定當前這份升級決議通過與否。
在這個例子裏面建置可升級合約的方式是透過 Hardhat Upgrades plugin,也就是我們只需要負責 Implement Contract 的部分,剩下就是在 deploy script 中使用 deployProxy
來部屬 Implement Contract。
佈署完之後接下來處理 ProxyAdmin 轉移,新開一個 script,使用 Hardhat 的套件 upgrades.admin.transferProxyAdminOwnership(MultiSigAddr);
來轉移。 如果我們今天是使用 gnosisSafe 就從上面通過升級決議(升級是一個 Proxy Contract 中的函式,要呼叫它一樣是一個交易),或者我們有其他 MultiSig 的 Pattern 就需要在上面通過升級這件事情的決議(是否要送出這筆交易)。
那如果是在我們今天這個例子裏面,並不是自己手刻一個 Proxy Contract,則也可以利用 Defender Plugin 來建置升級決議,並且在通過之後進行升級:defender.proposeUpgrade(proxyAddress, ImplentationV2);
。
最後歡迎大家拍打餵食大學生
0x2b83c71A59b926137D3E1f37EF20394d0495d72d