目录 开始让我们构建一些很酷的东西。什么是DAO? 为DAO设置客户端应用程序获取客户端代码获取设置。将连接到钱包添加到您的DAO仪表板。 创建会员NFT部署你的NFT包。部署NFT元数据。让用户铸造你的NFT。 创建代币治理部署ERC20合约。在DAODashboard上展示代币持有者。建库治理。让用户对提案进行投票。 收尾工作删除您的管理员权限并处理基本错误。完成并庆祝。建库治理 治理代币很酷,但如果人们不能用它来治理任何东西,它就有点没用了!我们接下来要做的是建立一个治理合约,让人们使用他们的代币对提案进行投票部署治理合约 我不想把这件事搞得太复杂。 归根结底,投票合约实际上是一种让人们对事物进行投票、自动计算这些票数的方式,然后任何成员都可以在链上执行提案。所有这些都不需要任何中心方。 例如,您可能想创建一个提案,例如将1000个令牌转移到EpicDesign5222以重新设计我们的登录页面。谁可以投票?人们需要投票多长时间?某人创建提案所需的最少代币数量是多少? 所有这些问题都在我们创建的初始投票合约中得到解答 这几乎就像你正在建立一个小国家,你需要建立你的初始政府投票系统! 前往8deployvote。js并添加以下内容:importsdkfrom。1initializesdk。(async(){try{constvoteContractAddressawaitsdk。deployer。deployVote({Giveyourgovernancecontractaname。name:MyamazingDAO,Thisisthelocationofourgovernancetoken,ourERC20contract!votingtokenaddress:INSERTTOKENADDRESS,Theseparametersarespecifiedinnumberofblocks。Assumingblocktimeofaround13。14seconds(forEthereum)Afteraproposaliscreated,whencanmembersstartvoting?Fornow,wesetthistoimmediately。votingdelayinblocks:0,Howlongdomembershavetovoteonaproposalwhenitscreated?wewillsetitto1day6570blocksvotingperiodinblocks:6570,Theminimumofthetotalsupplythatneedtovotefortheproposaltobevalidafterthetimefortheproposalhasended。votingquorumfraction:0,Whatstheminimumoftokensauserneedstobeallowedtocreateaproposal?Isetitto0。Meaningnotokensarerequiredforausertobeallowedtocreateaproposal。proposaltokenthreshold:0,});console。log(Successfullydeployedvotecontract,address:,voteContractAddress,);}catch(err){console。error(Failedtodeployvotecontract,err);}})(); 我们正在使用deployer。deployVote来实际建立合同。这将部署一个全新的投票合约! 注意我们如何给votingtokenaddress。这是我们的合约,它知道要接受哪个治理令牌。我们不希望人们随机尝试使用DOGE投票。 我们有votingdelayinblocks,如果您想在人们被允许对其进行投票之前给他们一些时间来审查提案,这可能会很有用。同样,我们有votingperiodinblocks,它只是指定一旦提案生效后某人必须多长时间投票,我们以区块的形式进行投票,这取决于您所在的区块链,可能需要更长的时间,对于以太坊Goerli,每13秒就有一个区块大约,所以平均来说,一天有6570个区块。 votingquorumfraction真的很有趣。假设一个成员创建了一个提案,而其他199名DAO成员正在迪斯尼世界度假并且不在线。好吧,在这种情况下,如果该DAO成员创建提案并对他们自己的提案投了YES票这意味着100的选票说YES(因为只有一票),一旦voteperiodinblocks结束,该提案就会通过!为了避免这种情况,我们使用了一个法定人数,上面写着为了使提案通过,必须在投票中使用至少x的代币。 为了举例说明,我们就用votingquorumfraction:0,这意味着无论投票中使用了多少百分比的代币,提案都会通过。这意味着如果其他成员正在度假,一个人在技术上可以自己通过提案,。就目前而言,这很好。您在现实世界中设置的法定人数取决于您的供应量以及您最初空投的数量。 最后,我们用proposaltokenthreshold:0允许任何人实际创建提案,即使他们持有零治理令牌。由你决定你想设置什么!让我们暂时将其保持为零。 继续并运行nodescripts8deployvote。js。这是我最终得到的:buildspacedaostarternodescripts8deployvote。jsSDKinitializedbyaddress:0xF11D6862e655b5F4e8f62E00471261D2f9c7E380Successfullydeployedvotecontract,address:0xE079991f3c26b832C3E8171F512694899E831eDB 这很酷。基本上,我们创建并部署了一个新的智能合约,让我们对链上的提案进行实际投票。这是一个标准的治理〔1〕合同。您可以在此处〔2〕查看您部署的确切合同。 如果你前往https:goerli。etherscan。io,你会在那里看到它!! 所以,现在我们有了三个合约:我们的NFT合约、我们的代币合约和我们的投票合约!请务必保存您的投票合约地址,我们稍后会再次使用它。设置你的金库 所以现在我们有了治理合同,我们可以对东西进行投票。真棒。但是有一个问题。 投票合约本身无法移动我们的代币。例如,假设我们现在想创建一个提案,例如向NarutoLover67发送1000美元HOKAGE以表彰其成为一名出色的成员。这实际上是行不通的。投票合约现在可以使用零代币。 为什么?因为您创建了代币供应。您的钱包拥有对整个供应的访问权限。所以只有你有权访问供应、移动代币、空投等等。基本上,这是一个独裁哈哈。这就是我们要做的我们将把90的代币转移到投票合约中。一旦我们的代币被转移到我们的投票合约中,投票合约本身就可以访问供应。 这将基本上成为我们的社区金库。 在这里,我只是选择了90作为随机。在实践中,这取决于。例如,以下是ENS分配方式: 他们决定将50的供应分配给他们的社区金库!每个DAO的代币经济学是如此不同,目前还没有标准的方式来做事。我非常喜欢ENS的做法。50在社区,25空投,另外25给核心团队贡献者。 前往9setupvote。js并添加以下内容:importsdkfrom。1initializesdk。(async(){try{Thisisourgovernancecontract。constvoteawaitsdk。getContract(INSERTVOTEADDRESS,vote);ThisisourERC20contract。consttokenawaitsdk。getContract(INSERTTOKENADDRESS,token);Giveourtreasurythepowertomintadditionaltokenifneeded。awaittoken。roles。grant(minter,vote。getAddress());console。log(Successfullygavevotecontractpermissionstoactontokencontract);}catch(error){console。error(failedtograntvotecontractpermissionsontokencontract,error);process。exit(1);}try{Thisisourgovernancecontract。constvoteawaitsdk。getContract(INSERTVOTEADDRESS,vote);ThisisourERC20contract。consttokenawaitsdk。getContract(INSERTTOKENADDRESS,token);Grabourwalletstokenbalance,rememberweholdbasicallytheentiresupplyrightnow!constownedTokenBalanceawaittoken。balanceOf(process。env。WALLETADDRESS);Grab90ofthesupplythatwehold。constownedAmountownedTokenBalance。displayVconstpercent90Number(ownedAmount)10090;Transfer90ofthesupplytoourvotingcontract。awaittoken。transfer(vote。getAddress(),percent90);console。log(Successfullytransferredpercent90tokenstovotecontract);}catch(err){console。error(failedtotransfertokenstovotecontract,err);}})(); 这里有一个非常简单的脚本!我们做两件事:1。我们使用token。balanceOf获取我们钱包中的总代币数量。请记住,除了我们空投的代币之外,现在我们的钱包基本上拥有全部供应。2。我们拿出我们拥有的总供应量,获得其中的90,然后使用token。transfer将这90转移到投票模块。如果您愿意,您可以100转让!但是,也许你想为自己这个创造者保留一些代币! 完成后,我们可以使用nodescripts9setupvote。js。这是我得到的输出:buildspacedaostarternodescripts9setupvote。jsSDKinitializedbyaddress:0xF11D6862e655b5F4e8f62E00471261D2f9c7E380SuccessfullygavevotemodulepermissionstoactontokenmoduleSuccessfullytransferred900000tokenstovotecontract 好的,准备好看史诗般的结果了吗?前往您的投票合同https:goerli。etherscan。io。单击令牌一词旁边的下拉菜单。在这里,你会看到我的合同上有844,527HOKAGE。 当我第一次看到它时,你会大吃一惊。我们确实有自己的金库。 注意:根据您的供应量和空投量,您的金库中可能有不同的金额。 让用户对提案进行投票创建你的DAO的前两个提案 酷。一切都准备好了,现在,我们只需要创建我们的第一个提案!前往10createvoteproposals。js并添加以下内容:importsdkfrom。1initializesdk。import{ethers}(async(){try{Thisisourgovernancecontract。constvoteawaitsdk。getContract(INSERTVOTEADDRESS,vote);ThisisourERC20contract。consttokenawaitsdk。getContract(INSERTTOKENADDRESS,token);Createproposaltomint420,000newtokentothetreasury。constamount420000;constdescriptionShouldtheDAOmintanadditionalamounttokensintothetreasury?;constexecutions〔{Ourtokencontractthatactuallyexecutesthemint。toAddress:token。getAddress(),OurnativeTokenisETH。nativeTokenValueistheamountofETHwewanttosendinthisproposal。Inthiscase,weresending0ETH。Werejustmintingnewtokenstothetreasury。So,setto0。nativeTokenValue:0,Weredoingamint!And,weremintingtothevote,whichisactingasourtreasury。inthiscase,weneedtouseethers。jstoconverttheamounttothecorrectformat。Thisisbecausetheamountitrequiresisinwei。transactionData:token。encoder。encode(mintTo,〔vote。getAddress(),ethers。utils。parseUnits(amount。toString(),18),〕),}〕;awaitvote。propose(description,executions);console。log(Successfullycreatedproposaltominttokens);}catch(error){console。error(failedtocreatefirstproposal,error);process。exit(1);}try{Thisisourgovernancecontract。constvoteawaitsdk。getContract(INSERTVOTEADDRESS,vote);ThisisourERC20contract。consttokenawaitsdk。getContract(INSERTTOKENADDRESS,token);Createproposaltotransferourselves6,900tokensforbeingawesome。constamount6900;constdescriptionShouldtheDAOtransferamounttokensfromthetreasurytoprocess。env。WALLETADDRESSforbeingawesome?;constexecutions〔{Again,weresendingourselves0ETH。Justsendingourowntoken。nativeTokenValue:0,transactionData:token。encoder。encode(Weredoingatransferfromthetreasurytoourwallet。transfer,〔process。env。WALLETADDRESS,ethers。utils。parseUnits(amount。toString(),18),〕),toAddress:token。getAddress(),},〕;awaitvote。propose(description,executions);console。log(Successfullycreatedproposaltorewardourselvesfromthetreasury,letshopepeoplevoteforit!);}catch(error){console。error(failedtocreatesecondproposal,error);}})(); 看起来很多。来吧,一步一步地阅读它!我们实际上正在创建两个新提案供成员投票: 1)我们正在创建一个提案,允许金库铸造420,000个新代币。你可以看到我们在代码中执行了了mint。 也许金库快用完了,我们想要更多的代币来奖励会员。请记住,早些时候我们赋予我们的投票合约铸造新代币的能力所以这很有效!这是一个民主的国库。如果您的成员认为这个提议很愚蠢并投反对票,这根本不会通过! 2)我们正在创建一个提案,将6,900代币从国库转移到我们的钱包。你可以看到我们在代码中执行力transfer。 也许我们做了一些好事并希望得到回报!在现实世界中,您通常会创建提案以向其他人发送代币。例如,也许有人帮助为DAO编写了一个新网站并希望获得奖励。我们可以转移他们的代币! 顺便说一句,我想针对nativeTokenValue做个说明。假设我们想让我们的提案说,我们想用2500个治理代币和0。1ETH奖励NarutoFangirl27帮助我们进行营销。这真的很酷!这意味着你可以用ETH和治理代币奖励人们两全其美。注意:如果我们想发送0。1ETH,那么0。1ETH就需要在我们的金库中! 当我运行nodescripts10createvoteproposals。js时,我得到:buildspacedaostarternodescripts10createvoteproposals。jsSDKinitializedbyaddress:0xF11D6862e655b5F4e8f62E00471261D2f9c7E380SuccessfullycreatedproposaltominttokensSuccessfullycreatedproposaltorewardourselvesfromthetreasury,letshopepeoplevoteforit! 酷。这就是我们的提案。我们要做的最后一件事就是让用户现在就从我们的DAO仪表板对提案进行投票! 注意:如果您设置了proposaltokenthreshold0代码可能会引发错误。你可能不得不将你的代币委托给投票合约(在相关的网络上),以便在部署提案之前发挥作用。让用户从仪表板对提案进行投票 最后,让我们把这一切带回家。现在,我们的提案基于我们的智能合约。但是,我们希望我们的用户能够轻松地看到他们并投票!让我们这样做。前往App。jsx。 继续并在token下添加:const{contract:vote}useContract(INSERTVOTEADDRESS,vote); 我们的网络应用程序需要访问我们的vote权限,以便用户可以与该合约进行交互。 从这里,让我们在shortenAddress函数下方的某处添加以下内容:const〔proposals,setProposals〕useState(〔〕);const〔isVoting,setIsVoting〕useState(false);const〔hasVoted,setHasVoted〕useState(false);Retrieveallourexistingproposalsfromthecontract。useEffect((){if(!hasClaimedNFT){}Asimplecalltovote。getAll()tograbtheproposals。constgetAllProposalsasync(){try{constproposalsawaitvote。getAll();setProposals(proposals);console。log(Proposals:,proposals);}catch(error){console。log(failedtogetproposals,error);}};getAllProposals();},〔hasClaimedNFT,vote〕);Wealsoneedtocheckiftheuseralreadyvoted。useEffect((){if(!hasClaimedNFT){}IfwehaventfinishedretrievingtheproposalsfromtheuseEffectabovethenwecantcheckiftheuservotedyet!if(!proposals。length){}constcheckIfUserHasVotedasync(){try{consthasVotedawaitvote。hasVoted(proposals〔0〕。proposalId,address);setHasVoted(hasVoted);if(hasVoted){console。log(Userhasalreadyvoted);}else{console。log(Userhasnotvotedyet);}}catch(error){console。error(Failedtocheckifwallethasvoted,error);}};checkIfUserHasVoted();},〔hasClaimedNFT,proposals,address,vote〕); 所以,我们在这里做两件事! 首先第一个useEffect,我们正在vote。getAll()获取存在于我们的治理合同中的所有提案,然后setProposals,这样我们可以稍后再渲染它们。 在第二个useEffect中,我们正在vote。hasVoted(proposals〔0〕。proposalId,address)检查该地址是否对第一个提案进行了投票。如果有,那么我们setHasVoted这样做是为了让用户不能再次投票!即使我们没有这个,如果用户尝试双重投票,我们的合约也会拒绝交易! thirdweb的魔力在于,它不仅让智能合约的部署变得非常容易,而且还让我们的客户端通过简单的功能与它们进行交互变得非常容易,比如vote。getAll()! 继续刷新您的页面,您应该会在旁边看到您的提案打印出来,您可以浏览所有数据! 如果您已经投票,您会看到如下内容: 下一段代码有点大,它涉及到实际渲染我们刚刚检索到的提案,以便用户可以有三个选项来投票:1。支持2。反对3。弃权 如果您熟悉ReactJS,您可以轻松地浏览它并自己弄清楚它是如何工作的。如果你不太了解ReactJS,请不要担心。只需复制粘贴即可。这并不丢人! 在现有导入之后添加AddressZero导入:import{AddressZero} 继续用这里〔3〕的代码替换if(hasClaimedNFT){}的内容。 当您检查您的网络应用程序时,您会看到如下内容: 很好。您现在可以实际使用这些按钮进行投票。 我们将治理合约设置为在24小时后停止投票。这意味着24小时后,如果:votesforproposalvotesagainstproposal 那么任何成员都可以通过我们的治理合约执行该提案。提案不能自动执行。但是,一旦提案通过,DAO的任何成员都可以触发被接受的提案。 例如,假设我们正在处理额外铸造420,000个代币的提案。如果votesforproposalvotesagainstproposal那么任何人都可以触发提案,然后我们的合约将铸造代币。有点疯狂,对吧?除了区块链,我们必须信任任何人。 想象一下,在一个腐败的国家,为某事投票,然后你的政府对你撒谎并说嘿,实际上我们没有得到足够的选票jk,而你真的这样做了,哈哈。或者,想象他们说,好吧,我们得到了足够的选票,我们会做到我们承诺的,但永远不会这样做! 在这种情况下,一切都被编码,代码不会说谎。 无论如何,现在不是讨论DAO如何潜在地改善我们的政府的时候;)。我们必须在此时此地完成我们的memeDAO!马上了。Github源码:https:github。combuildspacebuildspaceprojectstreemainJSDAO 原文链接:https:buildspace。sopbuilddaowithjavascript 引用链接 〔1〕治理:https:docs。openzeppelin。comcontracts4。xapigovernance?utmsourcebuildspace。soutmmediumbuildspaceproject 〔2〕此处:https:github。comthirdwebdevcontractsblobmaincontractsvoteVoteERC20。sol?utmsourcebuildspace。soutmmediumbuildspaceproject 〔3〕这里:https:github。combuildspacebuildspacedaofinalblobmainsrcApp。jsxL184?utmsourcebuildspace。soutmmediumbuildspaceproject