Compound 治理
Governance开发者文档
介绍
Compound 协议由 COMP Token 持有者管理和升级,使用三个不同的组件; COMP Token、治理模块(Governor Alpha)和 Timelock。这些合约一起,允许社区通过 cToken 或 Comproller 的管理功能提出、投票和实时更改。提案可以包括调整利率模型,到增加对新资产的支持等改变。
任何授权超过 10 万 COMP Token 的地址都可以提出治理活动,这些活动都是可执行的代码。提案产生后,社区可以在 3 天的投票期内提交投票。如果投票获得多数、且至少 40 万票以上,就会在 Timlock 中排队,2 天后可以实施。

COMP
COMP 是一个 ERC-20 代币,允许所有者将投票权委托给任何地址,包括自己的地址。所有者的代币余额会自动调整被委托人的投票权。
委托(Delegate)
将发送者的投票数委托给被委托人。用户每次可以委托给一个地址,被委托人的投票数相当于用户账号的 COMP 代币余额。投票将从当前区块开始进行委托,直到发送者再次委托或者转移其 COMP。
COMP
1 function delegate(address delegatee)
- delegatee : 发送者希望委托投票的地址;
- msg.sender : 试图委托投票的 COMP 代币持有者的地址;
- 返回值 : 无返回值,错误时恢复。
Solidity
1 Comp comp = Comp(0x123...); // contract address
2 comp.delegate(delegateeAddress);
Web3 1.2.6
1 const tx = await comp.methods.delegate(delegateeAddress).send({ from: sender });
签名委托(Delegate By Signature)
Delegate 投票从签名者到被委托人。这种方法与 Delegate 的目的相同,但它可以离线签名参与Compound 治理投票授权。有关如何创建离线签名的更多细节,请查看 EIP-712。
COMP
1 function delegateBySig(address delegateeAddress,uint256 nonce,uint256 expiry,uint256 v,uint256 r,uint256 s);
- delegatee : 发送者希望委托投票的地址;
- nonce : 合约状态要求与签名相匹配。这一点可以从合同的公共 nonce 映射中检索;
- expiry : 签名过期时间。以 unix epoch (uint) 后的秒数表示的区块时间戳;
- v : 签名恢复字节;
- r : ECDSA 签名对的一部分;
- s : ECDSA 签名对的一部分;
- 返回值 : 无,错误时恢复。
Solidity
1 Comp comp = Comp(0x123...); // contract address
2 comp.delegateBySig(delegateeAddress, nonce, expiry, v, r, s);
Web3 1.2.6
1 const tx = await comp.methods.delegateBySig(delegateeAddress, nonce, expiry, v, r, s).send({});
获取当前投票数
获取一个账户当前区块的票数余额。
COMP
1 function getCurrentVotes(address account) returns (uint96)
- account : 检索票数的账户地址;
- RETURN : 票数(整型)。
Solidity
1 Comp comp = Comp(0x123...); // contract address
2 uint votes = comp.getCurrentVotes(0xabc...);
Web3 1.2.6
1 const account = '0x123...'; // contract address
2 const votes = await comp.methods.getCurrentVotes(account).call();
获取先前投票数
获取一个账户在特定区块上的投票数。传入的区块高度必须是最终确定的区块,否则函数将revert。
COMP
1 function getPriorVotes(address account, uint blockNumber) returns (uint96)
- account : 检索先前的投票数的账户地址;
- blockNumber : 取回之前的票数的区块高度;
- 返回值 : 先前投票数;
Solidity
1 Comp comp = Comp(0x123...); // contract address
2 uint priorVotes = comp.getPriorVotes(account, blockNumber);
Web3 1.2.6
1 const priorVotes = await comp.methods.getPriorVotes(account, blockNumber).call();
Governor Alpha
Governor Alpha 是协议的治理模块;它允许超过 10 万 COMP 代币的地址提出修改建议。在提案开始时,手握投票权(通过调用 getpriorvotes 方法)的地址可以在 3 天的投票期内提交投票。如果提取被投了多数,且至少有 40 万票,则会在 Timelock 中排队,2 两天后实施。
Govern Alpha 合约包含一个守卫者地址,由 Compound 团队控制,可以取消提案,或在紧急情况下禁用治理模块。在最初的沙盒期后,守护者可以放弃权利,标志着 COMP 代币持有者开始完全控制。
法定投票数(Quorum Votes)
成功支持一项提案的最低票数要求。
Governor Alpha
1 function quorumVotes() public pure returns (uint)
- 返回值 : 成功支持一项提案的最低票数。
Solidity
1GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 uint quorum = gov.quorumVotes();
Web3 1.2.6
1 const quorum = await gov.methods.quorumVotes().call();
提案门槛(Proposal Threshold)
账户创建一个提案所需的最低票数;
Governor Alpha
1 function proposalThreshold() returns (uint)
- 返回值ETURN : 账户创建一个提案所需的最低票数。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 uint threshold = gov.proposalThreshold();
Web3 1.2.6
1 const threshold = await gov.methods.proposalThreshold().call();
提案最大操作量(Proposal Max Operations)
提案中可以包含的最大操作数量。操作是指提案成功并执行时将进行的函数调用。
Governor Alpha
1 function proposalMaxOperations() returns (uint)
- 返回值 : 提案中可以包含的最大操作数量。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 uint operations = gov.proposalMaxOperations();
Web3 1.2.6
1 const operations = await gov.methods.proposalMaxOperations().call();
投票延迟(Voting Delay)
在对一个提案投票前需要等到的以太坊区块数。当创建一个提案时,该值会被添加到当前区块高度上。
Governor Alpha
1function votingDelay() returns (uint)
- 返回值:在对一个提案投票前需要等到的以太坊区块数。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 uint blocks = gov.votingDelay();
Web3 1.2.6
1 const blocks = await gov.methods.votingDelay().call();
投票期(Voting Period)
以以太坊区块为单位,对提案进行投票的持续时间。
Governor Alpha
1 function votingPeriod() returns (uint)
- RETURN : 对提案进行投票的持续时间。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 uint blocks = gov.votingPeriod();
Web3 1.2.6
1 const blocks = await gov.methods.votingPeriod().call();
提案(Propose)
创建一个提案来改变协议。比如:一个提案能在 Comproller 上设置 cToken 的利率模型或者风险参数。
提案将委托投票人进行投票表决。如果在投票期结束前有足够的支持,提案将被自动颁布。已颁布的提案将在 Compound Timelock 合约中排队并执行。
发送者在紧邻的前一个区块中持有的 COMP 代币必须超过当前的提案阈值(proposalThreshold)。如果阈值是 10 万 COMP 代币,则发送者必须被委托超过所有 COMP 的 1% 才能创建一个提案。提案最多有 10 个操作(基于 proposalMaxOperations() )。
如果提案者当前有待处理或活动中的提案,则他们不能创建另一个提案。不可能在同一个区块中排队两个相同的操作(由于 Timelock 中的限制)。因此一个提案中的操作必须是唯一的,共享一个相同操作的唯一提案必须在不同的区块中排队。
Governor Alpha
1 function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) returns (uint)
- targets : 在提案执行过程中,将被调用的目标地址的有序列表。这个数组的长度必须和这个方法中所有其他数组参数的长度相同;
- values : 在提案执行过程中要传递给调用的值(即msg.value)的有序列表。这个数组的长度必须和这个方法中所有其他数组参数的长度相同;
- signatures : 提案执行过程中要传递的方法签名的有序列表。这个数组的长度必须和这个方法中所有其他数组参数的长度相同;
- calldatas : 在提案执行过程中要传递给每个方法调用的数据的有序列表。这个数组必须和这个方法中所有其他数组参数的长度相同;
- description : 对该提案的可读性说明及其将作出的修改;
- 返回值 : 新创建的提案 ID。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 uint proposalId = gov.propose(targets, values, signatures, calldatas, description);
Web3 1.2.6
1const tx = gov.methods.propose(targets, values, signatures, calldatas, description).send({ from: sender });
队列(Queue)
提案通过后,任何地址可以调用 queue 方法将提案转移到 Timelock 队列中。提案只有在成功通过后,才能被排队。
Governor Alpha
1 function queue(uint proposalId)
- proposalId : 已通过的提案 ID;
- 返回值 : 无,错误时将 revert。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 gov.queue(proposalId);
Web3 1.2.6
1 const tx = gov.methods.queue(proposalId).send({ from: sender });
执行(Execute)
在 Timelock 延迟期之后,任何账户都可以调用 execu 方法将提案中的更改应用到目标合约中。这将调用提案中描述的每一个操作。
这个功能是可支付(payable)的,所以 Timelock 合约可以调用提案中选择的可支付功能。例如,A 提案可以 像cETH 一样在市场上添加准备金,设置 cToken 的利率模型,或者在 Comptroller上设置风险参数。
Governor Alpha
1function execute(uint proposalId) payable returns (uint)
- proposalId : 要执行的成功提案 ID;
- 返回值 : 无,错误时将 revert。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 gov.execute(proposalId).value(999).gas(999)();
Web3 1.2.6
1 const tx = gov.methods.execute(proposalId).send({ from: sender, value: 1 });
取消(Cancel)
取消尚未执行的提案。除非提案人没有维持创建提案所需的委托量,否则监护人是唯一可以执行取消的人。如果提案人的委托人数没有超过提案的阈值,任何人都可以取消该提案。
Governor Alpha
1 function cancel(uint proposalId)
- proposalId : 要取消的提案 ID。提案不能重复取消。
- RETURN : 无,错误时 revert。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 gov.cancel(proposalId);
Web3 1.2.6
1 const tx = gov.methods.cancel(proposalId).send({ from: sender });
获取操作(Get Actions)
获取所选提案的操作列表。传递一个提案 ID,获取该提案的目标对象、值、签名和调用数据。
Governor Alpha
1 function getActions(uint proposalId) returns (uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas)
- proposalId : 拟获取操作的提案 ID。
- RETURN : 如果提案 ID 非法,则 revert 。否则成功时返回以下 4 个 参考信息:
- 提案调用的合约地址数组;
- 提案中使用的值 - 无符号整数数组;
- 提案签名的字符串数组;
- 提案的 calldata 字节数组。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 uint proposalId = 123;
3 (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) = gov.getActions(proposalId);
Web3 1.2.6
1 const {0: targets, 1: values, 2: signatures, 3: calldatas} = gov.methods.getActions(proposalId).call();
获取回执(Get Receipt)
获得指定投票者的提案选票。
Governor Alpha
1 function getReceipt(uint proposalId, address voter) returns (Receipt memory)
- proposalId : 提案ID,以获取投票者的选票收据;
- voter : 提案投票者的账户地址;
- RETURN : 错误时 revert。成功时返回投票者地址的选票收据结构数据。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 Receipt ballot = gov.getReceipt(proposalId, voterAddress);
Web3 1.2.6
1 const proposalId = 11;
2 const voterAddress = '0x123...';
3 const result = await gov.methods.getReceipt(proposalId, voterAddress).call();
4 const { hasVoted, support, votes } = result;
获取提案状态(State)
获取指定提案的提案状态。返回值 ProposalState 是一个 Governor Alpha 合约中定义的枚举类型。
Governor Alpha
1 function state(uint proposalId) returns (ProposalState)
-
proposalId : 提案ID,以获得其状态;
-
返回值 : 枚举类型 ProposalState。类型有:Pending(等待中)、Active(活动中)、
Canceled(已取消)、Defeated(已败北)、 Succeeded(已成功)、Queued(已排队)、Expired(已过期)和Executed(已执行)。
Solidity
1 overnorAlpha gov = GovernorAlpha(0x123...); // contract address
2 GovernorAlpha.ProposalState state = gov.state(123);
Web3 1.2.6
1 const proposalStates = ['Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'];
2 const proposalId = 123;
3 result = await gov.methods.state(proposalId).call();
4 const proposalState = proposalStates[result];
投票(Cast Vote)
对某项提案进行投票。该账户的表决权重取决于该账户在提案开始生效时的表决权重。
Governor Alpha
1 function castVote(uint proposalId, bool support)
- proposalId : 提案ID,以进行投票;
- support : 布尔值,赞同提案为 true ,反对为 false;
- RETURN : 无返回值,错误时 revert。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 gov.castVote(proposalId, true);
Web3 1.2.6
1 const tx = gov.methods.castVote(proposalId, false).send({ from: sender });
通过签名投票(Cast Vote By Signature)
对某项提案进行投票。该账户的投票权重由该账户在该提案状态生效时的投票权重决定。此方法的目的与 "cast vote "相同,但它可以让脱机签名参与 Compound 治理投票。有关如何创建离线签名的详细信息,请查看 EIP-712。
Governor Alpha
1 function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s)
- proposalId : 提案ID,以进行投票;
- support : 布尔值,赞同提案为 true ,反对为 false;
- v : 签名恢复字节;
- r : ECDSA 签名对的一部分;
- s : ECDSA 签名对的一部分;
- 返回值 : 无,错误时恢复。
Solidity
1 GovernorAlpha gov = GovernorAlpha(0x123...); // contract address
2 gov.castVoteBySig(proposalId, true, v, r, s);
Web3 1.2.6
1 const tx = await gov.methods.castVoteBySig(proposalId, false, v, r, s).send({});
时间锁(Timelock)
每个 cToken 合约和 Comptroller 合约都允许 Timelock 地址修改。Timelock 合约可以修改系统参数、逻辑和合约,以 "延迟时间、选择退出 " 的升级模式进行修改。
Timelock 有一个 2 天的最小延迟时间硬性规定,这是治理行动的最小通知时间。每项拟采取的治理行动,都会在公告后至少 2 天内公布。重大升级,如变更风险系统等,可能会有 14 天的延迟时间。
时间锁由治理模块控制;待定和已完成的治理行动可以在时间锁仪表板上监控。
暂停守护者(Pause Guardian)
Comptroller 合约指定了一个能够禁用协议功能的暂停守护者地址。暂停守护者只有在出现不可预见的漏洞时才会使用,暂停守护者只有一种能力:禁用一组选定的功能。Mint、Borrow、Borrow、Transfer 和 Liquidate。暂停守护者不能解除一个动作,也不能阻止用户调用赎回、偿还借款以平仓和退出协议。
目前,Compound 团队控制了暂停守护者的地址。