社区治理是 Defi 项目必备,Compound 的治理非常值得参考
- 原文链接, 作者:Adam Bavosa
社区治理已经取代了Compound协议管理员,这是朝着完全权力下放的重要一步。
权力下放的主要目标是使协议能够发展成为具有弹性的金融基础设施,而没有可知的弱点,也无需依赖任何团队。 通过这种方式,协议可以随着整个加密生态系统的增长而继续扩展,并且可以永久存在或至少伴随着以太坊。
本指南将向你介绍Compound的治理智能合约,并逐步引导你构建与治理系统交互的自定义功能和界面。
Compound协议只能由COMP代币持有者及其代理升级和配置。 协议的所有潜在更改,包括增加新市场或调整系统参数(如抵押因子或利率算法),都必须通过治理智能合约中指定的提案和投票过程。
COMP是在 Compound 治理中具有 1:1 投票权的代币。 以太坊钱包中的COMP代币持有人可以使用COMP 治理合约中提供的函数将投票权委托给自己或任何其他以太坊地址。
委托投票权的接收者(称为代理人),无论他们是COMP持有人本身还是其他地址,都可以提案、投票和执行提案以修改协议。 你可以在Compound治理面板上看到当前的代理列表。
为了收到COMP,请在以太坊或任何测试网上使用Compound协议。 有关更多详细信息,请查阅文档。
一旦了解了基础知识,就可以轻松构建用于治理的接口或扩展其功能。 要更深入地了解治理,请查看完整的文档。 为了快速入门,这里仅列出关键概念。
castVote
立即提交,也可以使用 castVoteBySig
离线签名稍后提交。 如果大多数票(达到 4%的委托COMP的法定人数,即400,000 COMP)对某个提案投票赞成,则该提案将在时间锁中排队。如果社区决定以治理的形式对其进行升级,则随着时间的推移,治理系统的这些关键组件可能会发生变化。 COMP持有者将是该协议各个方面未来方向的最终仲裁者。
注:COMP 部署在0xc00e94cb662c3520282e6f5717214004a7f26888 、 治理合约部署在0xc0dA01a04C3f3E0be433606045bB7017A7323E38
应用程序开发人员可以构建自己的自定义工作流和界面,以促进其用户和社区参与Compound治理。 例如,与Compound的利率市场集成的应用程序可能对添加治理功能感兴趣,包括:
此类接口将需要以下组件的组合:
castVoteBySig
功能,用户可以创建分配给其他用户的投票。 这将允许另一个用户代理他们提交投票(并支付gas),而无需委托给另一个用户。治理界面必须在区块链之间进行读写,我们将逐步介绍一些基本的JavaScript代码示例,以实现这两种功能。 读取数据将使用Compound API和Web3完成。 但是,只能使用Web3完成写入操作,例如委托或投票。
我们将按顺序演示如何执行以下每个操作:
GitHub上的Compound 治理快速入门代码库中提供了以下示例完整代码。
让我们根据其COMP余额按降序获取所有COMP代币持有者。 我们可以通过CompoundAPI的治理服务来实现。
let requestParameters = {
"page_size": 100, // number of results in a page
"network": "ropsten", // mainnet, ropsten
"order_by": "balance", // "votes", "balance", "proposals_created"
// "page_number": 1, // see subsequent response's `pagination_summary` to specify the next page
// addresses: ['0x123'], // array of addresses to filter on
// with_history: true, // boolean, returns a list of transaction history for the accounts
};
requestParameters = '?' + new URLSearchParams(requestParameters).toString();
fetch(`https://api.compound.finance/api/v2/governance/accounts${requestParameters}`)
.then((response) => response.json())
.then((result) => {
let accounts = result.accounts;
console.log(accounts);
let holders = [];
accounts.forEach((account) => {
holders.push({
"address": account.address,
"balance": parseFloat(account.balance).toFixed(4),
"delegate": account.delegate.address == 0 ? 'None' : account.delegate.address
});
});
holderListContainer.innerHTML = holderListTemplate(holders);
});
<center>文件:get_comp_holders_api.js 完整代码示例</center>
这段代码使用内置的浏览器获取方法,该方法返回JavaScript Promise。 代码库有一个相同的示例,在示例中使用Web3.js对COMP持有人进行直接区块链查询。 我们可以使用COMP智能合约找到相同的信息。
这两个代码示例的结果都是一个JSON对象数组,其中包含帐户地址,COMP代币余额和帐户委托地址。
[
{
"address": "0xb61c5971d9c0472befceffbe662555b78284c307",
"balance": "200000.0000",
"delegate": "0xb61c5971d9c0472befceffbe662555b78284c307",
}
]
<center>comp-holder.json </center>
我们可以看到委托了COMP的所有地址。 下面是一个区块链查询,它利用COMP合约的DelegateVotesChanged
事件来收集当前的每个委托。
// Ropsten COMP Contract
const compAddress = '0x1fe16de955718cfab7a44605458ab023838c2793';
const compAbi = window.compAbi;
const comp = new web3.eth.Contract(compAbi, compAddress);
(async () => {
const delegations = await comp.getPastEvents('DelegateVotesChanged', {
fromBlock: 0,
toBlock: 'latest'
});
const delegateAccounts = {};
delegations.forEach(e => {
const { delegate, newBalance } = e.returnValues;
delegateAccounts[delegate] = newBalance;
});
const delegates = [];
Object.keys(delegateAccounts).forEach((account) => {
const voteWeight = +delegateAccounts[account];
if (voteWeight === 0) return;
delegates.push({
delegate: account,
vote_weight: voteWeight
});
});
delegates.sort((a, b) => {
return a.vote_weight < b.vote_weight ? 1 : -1;
});
delegates.forEach(d => {
d.vote_weight = (100 * ((d.vote_weight / 1e18) / 10000000)).toFixed(6) + '%';
});
console.log(delegates);
delegateListContainer.innerHTML = delegateListTemplate(delegates);
})();
<center> get_delegates_web3.js 完整代码示例 </center>
或者,可以从Compound API 检索此信息。 这是Compound API 示例。 这些示例创建一个JSON对象数组,这些对象具有委托地址和COMP供给总量的投票权重(百分比)。
[
{
"delegate": "0xb61c5971d9c0472befceffbe662555b78284c307",
"vote_weight": "2.000000%"
}
]
<center> delegate.json </center>
如果你要创建治理浏览器,则获取所有提案非常有用。 Compound API 在这里很方便。 此示例将获取所有提案,无论其状态如何。 该API能够根据请求参数按状态过滤提案。
let requestParameters = {
"network": "ropsten", // mainnet, ropsten
"page_size": 100, // integer, defaults to 10
// "proposal_ids": 1, // an integer ID or array of IDs
// "state": "active", // "pending", "active", "canceled", "defeated", "succeeded", "queued", "expired", "executed"
// "with_detail": true, // boolean
// "page_number": 1, // see subsequent response's `pagination_summary` to specify the next page
};
requestParameters = '?' + new URLSearchParams(requestParameters).toString();
fetch(`https://api.compound.finance/api/v2/governance/proposals${requestParameters}`)
.then((response) => response.json())
.then((result) => {
console.log(result);
const proposals = result.proposals || [];
proposals.forEach((p) => {
p.state = p.states[p.states.length-1].state
p.for_votes = parseFloat(p.for_votes).toFixed(2);
p.against_votes = parseFloat(p.against_votes).toFixed(2);
});
proposalListContainer.innerHTML = proposalItemTemplate(proposals);
});
<center>get_all_proposals_api.js 完整代码示例</center>
我们也可以使用治理合约的ProposalCreated
事件直接通过Web3获取相同的提案数据。 代码示例使用以下提案数据创建一个JSON对象数组。
[
{
"against_votes": "601000.00",
"description": "10 BTC is actually a decent amount. Boop!",
"for_votes": "835000.00",
"id": 18,
"title": "Reduce WBTC reserves by 10!",
"state": "succeeded"
}
]
<center> proposal.json 完整代码示例</center>
一旦提案达到“有效”状态,选民就可以开始投票。 选票公开存储在区块链上,因此我们可以随时对其进行检索。 以下是在Ropsten上获取提案1的提交投票的示例。
// Ropsten Governor Alpha Contract
const governanceAddress = '0xc5bfed3bb38a3c4078d4f130f57ca4c560551d45';
const governanceAbi = window.governanceAbi;
const gov = new web3.eth.Contract(governanceAbi, governanceAddress);
(async () => {
const voteCastEvents = await gov.getPastEvents('VoteCast', {
fromBlock: 0,
toBlock: 'latest'
});
let submittedBallots = voteCastEvents;
let formattedBallots = [];
submittedBallots.forEach((ballot) => {
const { voter, support, votes, proposalId } = ballot.returnValues;
if (proposalId == id) {
formattedBallots.push({
blockNumber: ballot.blockNumber,
address: voter,
support: support ? 'In Favor' : 'Against',
votes: (parseFloat(votes) / 1e18).toFixed(2),
});
}
});
formattedBallots.reverse();
console.log(formattedBallots);
ballotListContainer.innerHTML = ballotListTemplate(formattedBallots);
})();
<center> get_all_ballots_web3.js</center>
提案ID从1开始按升序排列。要查看已提出的提案数量,可以从治理合约中获取 proposalCount
变量。 选票可以从Compound API中获取,例如在此治理服务示例中。 这是一个选票数据的JSON对象的结果数组。
[
{
"address": "0x9687eb285292cba14a60a0c77dfd36dd95b93889",
"support": "In Favor",
"votes": "8500000.00"
}
]
<center> ballot.json </center>
为了参与治理,COMP代币持有者必须delegate委托
其投票权。 可以委托到任何地址,包括代币持有者自己的地址。 一次只能委托一个地址。
以下是设置委托地址的Web3示例。 如果你没有COMP代币,则仍然可以委托,将来收到的COMP代币将自动委托给你选择的委托地址。
// Ropsten COMP Contract
compAddress = '0x1fe16de955718cfab7a44605458ab023838c2793';
compAbi = window.compAbi;
comp = new web3.eth.Contract(compAbi, compAddress);
let currentDelegate;
try {
currentDelegate = await comp.methods.delegates(myAccount).call();
} catch (e) {
currentDelegate = 0;
}
delegate.innerText = currentDelegate == 0 ? 'None' : currentDelegate;
submit.onclick = async () => {
const delegateTo = newDelegate.value;
if (!delegateTo) {
alert('Invalid address to delegate your votes.');
return;
}
loader.classList.remove('hidden');
try {
const tx = await comp.methods.delegate(delegateTo).send({ from: myAccount });
console.log(tx);
alert(`Successfully Delagated to ${delegateTo}`);
window.location.reload();
} catch(e) {
console.error(e);
alert(e.message);
}
loader.classList.add('hidden');
};
<center> delegate.js </center>
该代码依赖于启用了Web3的浏览器。 确保在浏览器中安装MetaMask。 可以从水龙头索取Ropsten ETH。 实际效果如下:
Compound治理最激动人心的部分是在更改协议的提案中投下你的一票。 委托者可以为每个有效的提案投反对票或赞成票。
以下代码示例展示了投票用户界面的功能。 如果Ropsten测试网上没有活动的提案,那么选择器中将没有任何提案!
// Ropsten Governor Contract
governanceAddress = '0xc5bfed3bb38a3c4078d4f130f57ca4c560551d45';
governanceAbi = window.governanceAbi;
gov = new web3.eth.Contract(governanceAbi, governanceAddress);
// Add a list of active proposals to the UI
getProposals();
// Get my account's vote weight
getMyVoteWeight(myAccount);
submit.onclick = async () => {
// An active proposal needs to be selected in the UI
if (proposalSelector.value < 1) {
alert('Select a proposal to vote in.');
return;
}
// A vote choice needs to be selected in the UI
if (!_for.checked && !against.checked) {
alert('Select a vote option.');
return;
}
const proposal = proposalSelector.value;
const vote = _for.checked ? true : false;
loader.classList.remove('hidden');
try {
const tx = await gov.methods.castVote(proposal, vote).send({ from: myAccount });
console.log(tx);
alert(`Successfully voted ${vote ? 'for' : 'against'} Proposal ${proposal}`);
window.location.reload();
} catch(e) {
console.error(e);
alert(e.message);
}
loader.classList.add('hidden');
};
<center> cast_vote.js </center>
社区治理的目标是使Compound协议成为对所有人开放和具有弹性的金融基础设施。 要达到这一里程碑,社区、开发人员、用户和机构都必须创建自己的界面和功能,以参与Compound治理。
由于以太坊具有可组合和开放访问的特性,因此任何人和所有人都可以创建自己的COMP和治理项目。 这里有更多资源可以帮助你入门。
本翻译由 Cell Network 赞助支持。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!