Compound 治理——提案
GovernorAlpha是社区治理投票的智能合约,包含提案部分和投票结果部分。
GovernorAlpha.sol
代码解析
function quorumVotes() public pure returns (uint) { return 25000000e18; }
function proposalThreshold() public pure returns (uint) { return 2500000e18; }
提案门槛,只有至少拥有这么多票的时候才能创建提案,这里是2500000,即至少拥有2500000个Tribe代币才能创建提案。function proposalMaxOperations() public pure returns (uint) { return 10; }
最多参数,即提案执行时可以执行的步骤数量,这里为10,意味着提案可以进行10个以内的外部调用。function votingDelay() public pure returns (uint) { return 3333; }
投票准备期,3333个区块后才能开始投票,约13.9小时后function votingPeriod() public pure returns (uint) { return 10000; }
投票周期10000个区块,约41.7小时uint public proposalCount
;
提案的数量mapping (uint => Proposal) public proposals;
根据提案id索引提案的键值对mapping (address => uint) public latestProposalIds;
用户的上个提案idbytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)");
Proposal
提案结构体
id
提案的id ,
proposer
发起人地址 ,
eta
提案执行的时间,初始为0.在加入队列时设定为当前区块时间+宽限时间,和队列的调用有关系,队列详见timelock合约。
targets
目标 ,提案通过后生效时执行的合约地址
values
值,提案生效后生效时传递的参数
signatures
签名,提案生效后生效时传递的签名
calldatas
回调参数,提案生效后生效时传递的回调参数
startBlock
开始投票区块,
endBlock
结束投票区块,
forVotes
支持票的数量,
againstVotes
反对票的数量,
canceled
作废,提案状态变量之一,只有调用作废方法时,该变量变为true,表示该提案作废,将无法进行别的操作
executed
生效,提案状态变量之一,初始为false,只有提案执行后才会变为true。
receipts
投票纪录。
其中targets
,values
,signatures
,calldatas
四个数组相同下标的为一组,代表着一次外部调用,这四个数组长度相同。笔者认为这里用结构体更合适。在提案通过后可以通过这些数组内的参数进行提案的执行。
struct Proposal {uint id;address proposer;uint eta;address[] targets;uint[] values;string[] signatures;bytes[] calldatas;uint startBlock;uint endBlock;uint forVotes;uint againstVotes;bool canceled;bool executed;mapping (address => Receipt) receipts;}
Receipt
结构体 投票记录
hasVoted 是否投票,标记某地址是否在此提案投过票,support 是否支持提案,votes所投的票数。
struct Receipt {bool hasVoted;bool support;uint96 votes;}
- ProposalState 提案状态
枚举类型,代表着提案的各个状态
enum ProposalState {Pending, //未开始状态Active, //活跃状态Canceled, //作废状态Defeated, //未通过状态Succeeded,//通过状态Queued, //待执行状态Expired, //过期状态Executed //执行状态}
constructor
构造方法
传入三个地址,时间锁地址,投票代币的地址,管理员的地址。
constructor(address timelock_, address tribe_, address guardian_) public {timelock = TimelockInterface(timelock_);tribe = TribeInterface(tribe_);guardian = guardian_;}
function propose
方法 创建提案
前提 创建人在上个区块的票数大于proposalThreshold。
前提 提案的执行的外物调用格式没问题。
前提 提案存在外部调用,不支持无外部调用的提案,即提案完成后必须可以执行。
前提 提案的外部调用不用能超过最大的外部调用,即10个。
获取该账户上个提案的id ,如果不为0即该账户之前有过提案且该提案在未开始或活跃状态则不允许创建新的提案。
设置开始区块为当前区块+投票准备期。
设置结束区块为投票开始区块+投票周期。
将总的提案数量+1,创建提案,并将提案存入id=>提案的mapping中,更新创建人地址中最后一个区块的id;
function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) public returns (uint) {require(tribe.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha: proposer votes below proposal threshold");require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha: proposal function information arity mismatch");require(targets.length != 0, "GovernorAlpha: must provide actions");require(targets.length <= proposalMaxOperations(), "GovernorAlpha: too many actions");uint latestProposalId = latestProposalIds[msg.sender];if (latestProposalId != 0) {ProposalState proposersLatestProposalState = state(latestProposalId);require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha: one live proposal per proposer, found an already active proposal");require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha: one live proposal per proposer, found an already pending proposal");}uint startBlock = add256(block.number, votingDelay());uint endBlock = add256(startBlock, votingPeriod());proposalCount++;Proposal memory newProposal = Proposal({id: proposalCount,proposer: msg.sender,eta: 0,targets: targets,values: values,signatures: signatures,calldatas: calldatas,startBlock: startBlock,endBlock: endBlock,forVotes: 0,againstVotes: 0,canceled: false,executed: false});proposals[newProposal.id] = newProposal;latestProposalIds[newProposal.proposer] = newProposal.id;emit ProposalCreated(newProposal.id, msg.sender, targets, values, signatures, calldatas, startBlock, endBlock, description);return newProposal.id;}
- function queue(uint proposalId)将提案加入队列
前提提案的状态是 Succeeded,通过状态
遍历本提案要执行的队列并将其加入时间锁队列中。
eta设置为 当前区块时间+宽限时间。
function queue(uint proposalId) public {require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha: proposal can only be queued if it is succeeded");Proposal storage proposal = proposals[proposalId];// solhint-disable-next-line not-rely-on-timeuint eta = add256(block.timestamp, timelock.delay());for (uint i = 0; i < proposal.targets.length; i++) {_queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);}proposal.eta = eta;emit ProposalQueued(proposalId, eta);}
function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta)
内部方法 将如果当前任务不在队列中则将任务加入队列中,已经执行过可以重复添加,但在本合约中应该没有这种调用。
function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal {require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha: proposal action already queued at eta");timelock.queueTransaction(target, value, signature, data, eta);}
function execute(uint proposalId)
执行提案
前提提案的状态是 Queued,待执行状态
遍历本提案要执行的队列并执行。
function execute(uint proposalId) public payable {require(state(proposalId) == ProposalState.Queued, "GovernorAlpha: proposal can only be executed if it is queued");Proposal storage proposal = proposals[proposalId];proposal.executed = true;for (uint i = 0; i < proposal.targets.length; i++) {timelock.executeTransaction{value : proposal.values[i]}(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);}emit ProposalExecuted(proposalId);}
function cancel(uint proposalId)
作废提案
未开始的提案或活跃的提案可以作废,只有管理员或者票数不够的情况下可以作废提案。
作废提案时同时作废队列中相应的提案部署方法。
function cancel(uint proposalId) public {ProposalState state = state(proposalId);require(state == ProposalState.Active || state == ProposalState.Pending, "GovernorAlpha: can only cancel Active or Pending Proposal");Proposal storage proposal = proposals[proposalId];require(msg.sender == guardian || tribe.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha: proposer above threshold");proposal.canceled = true;for (uint i = 0; i < proposal.targets.length; i++) {timelock.cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);}emit ProposalCanceled(proposalId);}
function getActions(uint proposalId)
获取提案的信息,这些信息是执行提案的重要信息
传入提案的id,返回提案的 targets, values,signatures,calldatas
function getActions(uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {Proposal storage p = proposals[proposalId];return (p.targets, p.values, p.signatures, p.calldatas);}
function getReceipt(uint proposalId, address voter)
获取某地址对某个提案的投票结果,传入提案的id和地址的id
function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) {return proposals[proposalId].receipts[voter];}
function state(uint proposalId)
返回提案的状态
如果提案canceled设置为true
则为作废状态
,作废状态是提案的终极状态之一。
如果状态未确定且当前区块小于开始区块
,则为未开始状态
,未开始的提案
如果状态未确定且当前区块小于结束区块
,则为活跃状态
如果状态未确定且获得的票数小于标记票数,或者小于平均票数
则为未通过状态
如果状态未确定且eta 等于0
则为通过状态
,此时提案还未加入执行队列
如果状态未确定且合约的执行为true
则为执行状态
,即合约为生效状态
如果状态未确定且区块的时间大于 eta+宽限期
则为过期状态
否则为待执行状态
function state(uint proposalId) public view returns (ProposalState) {require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha: invalid proposal id");Proposal storage proposal = proposals[proposalId];if (proposal.canceled) {return ProposalState.Canceled;} else if (block.number <= proposal.startBlock) {return ProposalState.Pending;} else if (block.number <= proposal.endBlock) {return ProposalState.Active;} else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) {return ProposalState.Defeated;} else if (proposal.eta == 0) {return ProposalState.Succeeded;} else if (proposal.executed) {return ProposalState.Executed;// solhint-disable-next-line not-rely-on-time} else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {return ProposalState.Expired;} else {return ProposalState.Queued;}}
function castVote
投票,参数提案的id,是否支持
function castVote(uint proposalId, bool support) public {return _castVote(msg.sender, proposalId, support);}
function castVoteBySig
使用签名进行投票
function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public {bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));address signatory = ecrecover(digest, v, r, s);require(signatory != address(0), "GovernorAlpha: invalid signature");return _castVote(signatory, proposalId, support);}
function _castVote
投票
检查,只有活跃状态的提案可以投票
获取voter的投票纪录,如果已经投过票则不允许再次投票
获取voter在提案开始区块的票数
根据support的结果往forVotes或againstVotes增加票数。
纪录voter在此提案的投票纪录。
function _castVote(address voter, uint proposalId, bool support) internal {require(state(proposalId) == ProposalState.Active, "GovernorAlpha: voting is closed");Proposal storage proposal = proposals[proposalId];Receipt storage receipt = proposal.receipts[voter];require(receipt.hasVoted == false, "GovernorAlpha: voter already voted");uint96 votes = tribe.getPriorVotes(voter, proposal.startBlock);if (support) {proposal.forVotes = add256(proposal.forVotes, votes);} else {proposal.againstVotes = add256(proposal.againstVotes, votes);}receipt.hasVoted = true;receipt.support = support;receipt.votes = votes;emit VoteCast(voter, proposalId, support, votes);}
function __acceptAdmin()
内部方法 任职时间锁的管理员,只有管理员能够调用。
function __acceptAdmin() public {require(msg.sender == guardian, "GovernorAlpha: sender must be gov guardian");timelock.acceptAdmin();}
function __abdicate()
内部方法辞职,设置管理员为0地址。
function __abdicate() public {require(msg.sender == guardian, "GovernorAlpha: sender must be gov guardian");guardian = address(0);}
function __transferGuardian(address newGuardian)
转移管理员
function __transferGuardian(address newGuardian) public {require(msg.sender == guardian, "GovernorAlpha: sender must be gov guardian");guardian = newGuardian;}
function __queueSetTimelockPendingAdmin
在队列中添加时间锁的设置待定管理员方法,并将时间锁的待定管理员设置为newPendingAdmin。
function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {require(msg.sender == guardian, "GovernorAlpha: sender must be gov guardian");timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);}
function __executeSetTimelockPendingAdmin
内部函数
在队列中执行时间锁的设置待定管理员方法,并将时间锁的待定管理员设置为newPendingAdmin。
function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public {require(msg.sender == guardian, "GovernorAlpha: sender must be gov guardian");timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta);}
function add256 function sub256
安全加减函数
function add256(uint256 a, uint256 b) internal pure returns (uint) {uint c = a + b;require(c >= a, "addition overflow");return c;}function sub256(uint256 a, uint256 b) internal pure returns (uint) {require(b <= a, "subtraction underflow");return a - b;}
function getChainId()
方法获取链id
function getChainId() internal pure returns (uint) {uint chainId;// solhint-disable-next-line no-inline-assemblyassembly { chainId := chainid() }return chainId;}
Compound 治理——提案相关推荐
- Bounce宣布关于BOT代币迁移的链上治理提案已通过
据官方消息,去中心化拍卖工具Bounce宣布,关于BOT代币迁移的链上治理提案已经成功通过.官方称,从BOT代币到AUCTION代币的迁移标志着Bounce去中心化拍卖和Bounce认证拍卖的新旅程即 ...
- Compound 治理——执行队列 TimeLock
简介 对应的代码TimeLock.sol TimeLock.sol 直译为时间锁.其实是一个在solidity中实现的队列. 可以将代码加入队列,从队列中执行.执行方式为call调用.不支持重复调用, ...
- 用于对Compound协议进行无Gas投票和委托的社区工具发布
据链闻消息,加密货币爱好者Arr00宣布与开发者Anish Agnihotri共同推出一个用于对Compound协议进行无Gas投票和委托的社区工具COMP.vote.通过COMP.vote中的签名投 ...
- Compound原理
1 Compound 1.1 Compound是什么 Compound是DeFi上的借贷市场,这个借贷市场提供了存币和借币的功能,就像一个银行,用户可以存币并获得利息收益,或进行抵押借币. Compo ...
- 烤星 · DeFi 课堂丨如何理解 DeFi 项目中的治理代币?(上)
王也 打开 DeFi 世界之门,从小白到 DeFi 大神的进阶必修课. 由 Conflux × Odaily星球日报联合出品,星球日报资深记者@王也担任主笔. 烤星 · DeFi 课堂 6 月 16 ...
- Python之父重回决策层,社区治理新方案!
在Guido van Rossum(吉多·范罗苏姆)卸任BDFL("终身仁慈独裁者")一职半年多之后,Python社区迎来了新的治理新方案:指导委员会模式,而经过投票Guido v ...
- python 社区-谈谈Python社区7种治理方案
原标题:谈谈Python社区7种治理方案 之前Python之父Guido van Rossum突然决定卸任,我写过一篇 Python 之父为什么要退出决策层?以及我的思考 谈了下自己的一些感想.这段时 ...
- 从Uniswap Discord社区看DeFi链下治理的三大难题 |链捕手
Unsiwap作为DeFi市场最具代表性的项目,其治理过程也吸引了外界的广泛关注. 近期,Toby Shorin等人在以太坊基金会与Uniswap赠款计划的支持下,对Uniswap Discord社区 ...
- Moonbeam社区论坛:一个致力于社区讨论、提案、和反馈的空间
欢迎Moonbeam社区来到Moonbeam社区论坛,这是一个致力于举办与Moonbeam和Moonriver相关讨论的全新社区空间,讨论话题包括技术发展.治理.和未来生态系统计划.该论坛还将作为更新 ...
最新文章
- 在腾讯云上创建您的SQL Cluster(3)
- python读取excel日期内容读出来是数字-Python读取Excel,日期列读出来是数字的处理...
- mac下Android开发环境配置
- 多项式的ln、exp、快速幂和开根学习小记
- ITK:基本区域增长
- FineReport连接mysql8.0.16
- 当AI成为基础资源,360OS 发力在线教育的重心——专访360OS张焰
- 体积最小桌面linux,Tiny Core Linux - 体积最小的精简 Linux 操作系统发行版之一 (仅10多MB)...
- ajax常见的status状态码
- 小腿抽筋了,按摩承山穴,外加念观世音菩萨是不是迷信
- mysql 索引 范围扫描_MySql学习笔记(六):扫描范围
- 杰控组态软件MODBUS寄存器地址的设置
- 用python绘制用例图源代码_用Visio画UML用例图
- 3种方式构造HTTP请求详解(HTTP4)
- 怎么部署项目解析域名_内部解析服务器部署
- java url 收集
- 通过Mycat分库分表
- 添加Kinect模型及kinect.dae文件的下载
- 电磁场与电磁波(1)——简史
- 脑机实验室系列 | 浙大求是高等研究院脑机接口研究所
热门文章
- oracle heavy swapping,WARNING: Heavy swapping observed on system in last 5 mins
- android工具类怎么写,用kotlin写了一些android常用的一些工具类
- 竞争性谈判和竞争性磋商的区别
- 如何使用 R 语言来绘制图表和图(曲线等)
- bert 环境搭建之PytorchTransformer 安装
- 全链路稳定性背后的数字化支撑:阿里巴巴鹰眼技术解密
- python热图_Python 绘制热图
- ST2Vec: Spatio-Temporal Trajectory Similarity Learning in Road Networks
- java无穷大 inf_Java正无穷大、负无穷大以及NaN
- S03_CH02_AXI_DMA PL发送数据到PS