本篇内容主要讲解“Story DAO的白名单和测试方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Story DAO的白名单和测试方法是什么”吧!
让我们用这个骨架创建一个新的合约StoryDao.sol
:
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // hundredths of a percent, i.e. 100 is 1% uint256 public whitelistfee = 10000000000000000; // in Wei, this is 0.01 ether event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // duration of story's chapter in days uint256 public durationSubmissions = 1000; // duration of story's chapter in entries function changedaofee(uint256 _fee) onlyOwner external { require(_fee < daofee, "New fee must be lower than old fee."); daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee < whitelistfee, "New fee must be lower than old fee."); whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function lowerSubmissionFee(uint256 _fee) onlyOwner external { require(_fee < submissionZeroFee, "New fee must be lower than old fee."); submissionZeroFee = _fee; emit SubmissionFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
我们正在导入SafeMath
以便再次进行安全计算,但这次我们还使用了Zeppelin的Ownable
合约,该合约允许某人“拥有”故事并执行某些仅限管理员的功能。简单地说我们的StoryDao is Ownable
就够了;随时检查合约,看看它是如何工作的。
我们还使用此合约中的onlyOwner
修饰符。函数修饰符基本上是函数扩展和插件。onlyOwner
修饰符如下所示:
modifier onlyOwner() { require(msg.sender == owner); _; }
当onlyOwner
被添加到一个函数中时,那个函数的体被粘贴到_;
所在的部分,并且它比其他的一切内容都先执行。因此,通过使用此修饰符,该函数会自动检查邮件发件人是否也是合约的所有者,然后照常继续(如果是)。如果没有,它会崩溃。
通过在改变我们的Story DAO的费用和其他参数的函数上使用onlyOwner
修饰符,我们确保只有管理员才能进行这些更改。
让我们测试一下初始函数。
如果文件夹test
不存在,请创建它。然后在其中创建文件TestStoryDao.sol
和TestStoryDao.js
。因为在Truffle中没有本地方法来测试异常,所以也可以使用以下内容创建helpers/expectThrow.js
:
export default async promise => { try { await promise; } catch (error) { const invalidOpcode = error.message.search('invalid opcode') >= 0; const outOfGas = error.message.search('out of gas') >= 0; const revert = error.message.search('revert') >= 0; assert( invalidOpcode || outOfGas || revert, 'Expected throw, got \'' + error + '\' instead', ); return; } assert.fail('Expected throw not received'); };
注意:Solidity测试通常用于测试基于合约的低级函数,即智能合约的内部。JS测试通常用于测试合约是否可以与外部进行正确的交互,这是我们最终用户将要做的事情。
在TestStoryDao.sol
,输入以下内容:
pragma solidity ^0.4.24; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/StoryDao.sol"; contract TestStoryDao { function testDeploymentIsFine() public { StoryDao sd = StoryDao(DeployedAddresses.StoryDao()); uint256 daofee = 100; // hundredths of a percent, i.e. 100 is 1% uint256 whitelistfee = 10000000000000000; // in Wei, this is 0.01 ether uint256 durationDays = 21; // duration of story's chapter in days uint256 durationSubmissions = 1000; // duration of story's chapter in entries Assert.equal(sd.daofee(), daofee, "Initial DAO fee should be 100"); Assert.equal(sd.whitelistfee(), whitelistfee, "Initial whitelisting fee should be 0.01 ether"); Assert.equal(sd.durationDays(), durationDays, "Initial day duration should be set to 3 weeks"); Assert.equal(sd.durationSubmissions(), durationSubmissions, "Initial submission duration should be set to 1000 entries"); } }
这将检查StoryDao合约是否正确部署,并提供正确的费用和持续时间。第一行确保通过从已部署地址列表中读取它来部署它,并且最后一节做了一些断言——检查声明是真还是假。在我们的例子中,我们将数字与已部署合约的初始值进行比较。每当它为“true”时,Assert.equals
部分将发出一个“True”的事件,这是Truffle在测试时正在监听的事件。
在TestStoryDao.js
,输入以下内容:
import expectThrow from './helpers/expectThrow'; const StoryDao = artifacts.require("StoryDao"); contract('StoryDao Test', async (accounts) => { it("should make sure environment is OK by checking that the first 3 accounts have over 20 eth", async () =>{ assert.equal(web3.eth.getBalance(accounts[0]).toNumber() > 2e+19, true, "Account 0 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[1]).toNumber() > 2e+19, true, "Account 1 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[2]).toNumber() > 2e+19, true, "Account 2 has more than 20 eth"); }); it("should make the deployer the owner", async () => { let instance = await StoryDao.deployed(); assert.equal(await instance.owner(), accounts[0]); }); it("should let owner change fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; instance.changedaofee(newDaoFee, {from: accounts[0]}); instance.changewhitelistfee(newWhitelistFee, {from: accounts[0]}); instance.changedurationdays(newDayDuration, {from: accounts[0]}); instance.changedurationsubmissions(newSubsDuration, {from: accounts[0]}); assert.equal(await instance.daofee(), newDaoFee); assert.equal(await instance.whitelistfee(), newWhitelistFee); assert.equal(await instance.durationDays(), newDayDuration); assert.equal(await instance.durationSubmissions(), newSubsDuration); }); it("should forbid non-owners from changing fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; await expectThrow(instance.changedaofee(newDaoFee, {from: accounts[1]})); await expectThrow(instance.changewhitelistfee(newWhitelistFee, {from: accounts[1]})); await expectThrow(instance.changedurationdays(newDayDuration, {from: accounts[1]})); await expectThrow(instance.changedurationsubmissions(newSubsDuration, {from: accounts[1]})); }); it("should make sure the owner can only change fees and duration to valid values", async () =>{ let instance = await StoryDao.deployed(); let invalidDaoFee = 20000; let invalidDayDuration = 0; let invalidSubsDuration = 98; await expectThrow(instance.changedaofee(invalidDaoFee, {from: accounts[0]})); await expectThrow(instance.changedurationdays(invalidDayDuration, {from: accounts[0]})); await expectThrow(instance.changedurationsubmissions(invalidSubsDuration, {from: accounts[0]})); }) });
为了使我们的测试成功运行,我们还需要告诉Truffle我们想要部署StoryDao——因为它不会为我们做。因此,让我们在migrations
创建3_deploy_storydao.js
,其内容几乎与我们之前编写的迁移相同:
var Migrations = artifacts.require("./Migrations.sol"); var StoryDao = artifacts.require("./StoryDao.sol"); module.exports = function(deployer, network, accounts) { if (network == "development") { deployer.deploy(StoryDao, {from: accounts[0]}); } else { deployer.deploy(StoryDao); } };
此时,我们还应该在项目文件夹的根目录中更新(或创建,如果它不存在)package.json
文件,其中包含我们目前所需的依赖项,并且可能在不久的将来需要:
{ "name": "storydao", "devDependencies": { "babel-preset-es2015": "^6.18.0", "babel-preset-stage-2": "^6.24.1", "babel-preset-stage-3": "^6.17.0", "babel-polyfill": "^6.26.0", "babel-register": "^6.23.0", "dotenv": "^6.0.0", "truffle": "^4.1.12", "openzeppelin-solidity": "^1.10.0", "openzeppelin-solidity-metadata": "^1.2.0", "openzeppelin-zos": "", "truffle-wallet-provider": "^0.0.5", "ethereumjs-wallet": "^0.6.0", "web3": "^1.0.0-beta.34", "truffle-assertions": "^0.3.1" } }
和.babelrc
文件的内容:
{ "presets": ["es2015", "stage-2", "stage-3"] }
我们还需要在我们的Truffle配置中要求Babel,因此它知道它应该在编译时使用它。
注意:Babel是NodeJS的一个附加组件,它允许我们在当前一代NodeJS中使用下一代JavaScript,因此我们可以编写诸如import
。如果这超出了你的理解范围,只需忽略它,然后只需逐字粘贴即可。在以这种方式安装后,你可能永远不必再处理这个问题。
require('dotenv').config(); ================== ADD THESE TWO LINES ================ require('babel-register'); require('babel-polyfill'); ======================================================= const WalletProvider = require("truffle-wallet-provider"); const Wallet = require('ethereumjs-wallet'); // ...
现在,终于进行truffle test
。输出应该类似于这个:
有关测试的更多信息,请参阅本教程,该教程专门用于测试智能合约。
在本课程的后续部分中,我们将跳过测试,因为输入它们会使教程太长,但请参考项目的最终源代码来检查它们。我们刚刚完成的过程已经设置了测试环境,因此你可以在进一步设置的情况下编写测试。
现在让我们构建一个白名单机制,让用户参与构建Story。将以下函数框架添加到StoryDao.sol
:
function whitelistAddress(address _add) public payable { // whitelist sender if enough money was sent } function() external payable { // if not whitelisted, whitelist if paid enough // if whitelisted, but X tokens at X price for amount }
未命名的函数function()
被称为回调函数,这是在没有特定指令的情况下将钱发送到此合约时被调用的函数(即,没有专门调用其他函数)。这可以让人们加入StoryDao,只需将以太发送到DAO并立即将其列入白名单,或者购买代币,具体取决于它们是否已经列入白名单。
whitelistSender
功能用于白名单,可以直接调用,但是如果发送方尚未列入白名单,我们将确保当收到一些以太时,后备功能会自动调用它。whitelistAddress
函数被声明为public
因为它也应该可以从其他合约中调用,并且回调函数是external
函数,因为money
将仅从外部地址转到此地址。调用此合约的合约可以直接轻松调用所需的功能。
让我们首先处理回调函数。
function() external payable { if (!whitelist[msg.sender]) { whitelistAddress(msg.sender); } else { // buyTokens(msg.sender, msg.value); } }
我们检查发件人是否已经在白名单中,并将调用委托给whitelistAddress
函数。请注意,我们已经注释掉了buyTokens
函数,因为我们还没有它。
接下来,让我们处理白名单。
function whitelistAddress(address _add) public payable { require(!whitelist[_add], "Candidate must not be whitelisted."); require(!blacklist[_add], "Candidate must not be blacklisted."); require(msg.value >= whitelistfee, "Sender must send enough ether to cover the whitelisting fee."); whitelist[_add] = true; whitelistedNumber++; emit Whitelisted(_add, true); if (msg.value > whitelistfee) { // buyTokens(_add, msg.value.sub(whitelistfee)); } }
请注意,此函数接受地址作为参数,并且不从消息中提取它(来自交易)。如果有人无法承担加入DAO的费用,这还有一个额外的好处,即人们可以将其他人列入白名单。
我们通过一些健壮性检查启动该功能:发件人不得列入白名单或列入黑名单(禁止),并且必须已发送足够的费用以支付费用。如果这些条件令人满意,则将地址添加到白名单中,发出白名单事件,最后,如果发送的以太数量大于覆盖白名单费用所需的以太数量,则剩余部分用于买这些代币。
注意:我们使用sub
而不是-
来减,因为这是一个安全计算的SafeMath
函数。
用户现在可以将自己或其他人列入白名单,只要他们向StoryDao合约发送0.01以太或更多。
到此,相信大家对“Story DAO的白名单和测试方法是什么”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。