• 以太坊dapp开发Learn Ethereum Dapp in this full video course for beginners from EatTheBlocks. This course teaches how to build decentralized applications on the Ethereum Blockchain. 在此完整的视频课程...
以太坊dapp开发Learn Ethereum Dapp in this full video course for beginners from EatTheBlocks. This course teaches how to build decentralized applications on the Ethereum Blockchain.  在此完整的视频课程中，学习来自EatTheBlocks的初学者的以太坊Dapp。 本课程讲授如何在以太坊区块链上构建去中心化应用程序。
You'll learn how to build 5 Ethereum Dapps. For each of them, this course demonstrates how to: 您将学习如何构建5个以太坊Dapp。 对于他们每个人，本课程都演示了如何：
develop smart contracts, 开发智能合约 test smart contracts, 测试智能合约， and build a web frontend to interact with the smart contract. 并构建一个与智能合约进行交互的Web前端。 The course covers: 该课程涵盖：
The architecture of Dapps Dapps的架构 Solidity, the programming language for Ethereum smart contract Solidity，以太坊智能合约的编程语言 Remix, the online IDE for Solidity Remix，Solidity在线IDE Truffle, the framework for Solidity smart contracts 松露，Solidity智能合约的框架 Ganache, the local development Blockchain Ganache，本地发展区块链 Web3, the JS library to integrate a Dapp frontend Web3，用于集成Dapp前端的JS库 Metamask, the Ethereum wallet used by most Dapps (browser extension) Metamask，大多数Dapp使用的以太坊钱包(浏览器扩展) Deployment to public testnet (Ropsten) and mainnet, using Infura API 使用Infura API部署到公共测试网(Ropsten)和主网 Watch the full course below or on the freeCodeCamp YouTube channel (5 hour watch). 在下面或在freeCodeCamp YouTube频道上观看完整的课程(观看5小时)。
翻译自: https://www.freecodecamp.org/news/learn-how-to-build-ethereum-dapp-and-develop-for-the-blockchain/以太坊dapp开发
展开全文
• 以太坊dappIn part 7 of this tutorial series on building DApps with Ethereum, we showed how to build the app’s front end, setting up and deploying the UI for this story we’ve been working on. ...
以太坊dappIn part 7 of this tutorial series on building DApps with Ethereum, we showed how to build the app’s front end, setting up and deploying the UI for this story we’ve been working on.
在本教程系列的第7部分中 ，该系列教程介绍了如何使用以太坊构建DApp，如何构建应用程序的前端，如何设置和部署我们正在研究的UI。
It’s time to do some deploying and write a few final functions.
现在该进行一些部署并编写一些最终功能了。

This is the eighth part of a series on building decentralized applications using the Ethereum blockchain. The project we’re building is called The Neverending Story. The full project can be found at storydao.bitfalls.com. Its full code is on GitHub.
这是使用以太坊区块链构建去中心化应用程序系列的第八部分。 我们正在构建的项目称为“永无止境的故事”。 完整的项目可以在storydao.bitfalls.com上找到。 其完整代码在GitHub上 。
Here is an outline of the whole series:
这是整个系列的概述：
In part 1, we bootstrap two versions of a local blockchain for development: a Ganache version, and a full private PoA version. 在第1部分中 ，我们引导了用于开发的本地区块链的两个版本：Ganache版本和完整的私有PoA版本。 In part 2, we build and deploy our TNS token. 在第2部分中 ，我们构建和部署TNS令牌。 In part 3, we look at how to compile, deploy, test and verify our custom TNS token, which is compatible with all exchanges and can be used as a regular ERC20 token. 在第3部分中 ，我们将研究如何编译，部署，测试和验证我们的自定义TNS令牌，该令牌与所有交易所兼容，并且可以用作常规ERC20令牌。 In part 4, we take the first steps towards developing a Story DAO, covering whitelisting and testing. 在第4部分中 ，我们迈出了开发Story DAO的第一步，涵盖了白名单和测试。 In part 5, we deal with adding content to the story, looking at how to add the ability for participants to buy tokens from the DAO and to add submissions into the story. 在第5部分中 ，我们处理了向故事中添加内容的问题，探讨了如何增加参与者从DAO购买令牌以及向故事中添加提交内容的能力。 In part 6, we take the DAO to its final form, adding voting, blacklisting/unblacklisting, and dividend distribution and withdrawal, while throwing in some additional helper functions for good measure. 在第6部分中 ，我们将DAO转换为最终形式，添加了投票，将其列入黑名单/取消黑名单以及股息分配和提款，同时还添加了一些额外的辅助功能，以达到很好的效果。 In part 7, we show how to build the app’s front end, setting up and deploying the UI for this story we’ve been working on. 在第7部分中 ，我们展示了如何构建应用程序的前端，如何为我们一直在研究的这个故事设置和部署UI。 In this final part, we look at the final steps of deploying the app and writing a few final functions. 在最后一部分中，我们介绍了部署应用程序和编写一些最终功能的最后步骤。

自杀 (Suicide)
Something could go very, very wrong and the whole DAO somehow get destroyed — either through hacks of bad and hastily written code, or through the inability to make long loops due to too many participants. (Too many voters on a proposal might as well break the system; we really didn’t put any precaution in place for that!) Just in case this happens, it might be useful to have the equivalent of a “big red button”. First, let’s upgrade our StoryDao:
可能会发生非常非常非常错误的情况，并且整个DAO都会以某种方式被破坏-通过黑客编写糟糕且草率的代码，或者由于参与者过多而导致无法进行长循环。 (提案中的选民太多可能会破坏系统；我们确实没有为此采取任何预防措施！)以防万一，这种情况下，相当于“红色大按钮”可能会很有用。 首先，让我们升级StoryDao：
function bigRedButton() onlyOwner external {
active = false;
withdrawToOwner();
token.unlockForAll();
}
Then, let’s make it possible to unlock all the tokens at once in our Token contract:
然后，让我们有可能一次在我们的令牌合约中解锁所有令牌：
/**
@dev unlocks the tokens of every user who ever had any tokens locked for them
*/
function unlockForAll() public onlyOwner {
uint256 len = touchedByLock.length;
for (uint256 i = 0; i < len; i++) {
locked[touchedByLock[i]] = 0;
}
}
Naturally, we need to add this new list of addresses into the contract:
当然，我们需要将此新地址列表添加到合同中：
address[] touchedByLock;
And we need to upgrade our increaseLockedAmount function to add addresses to this list:
而且，我们需要升级我们的increaseLockedAmount函数，以将地址添加到此列表中：
/**
@dev _owner will be prevented from sending _amount of tokens. Anything
beyond this amount will be spendable.
*/
function increaseLockedAmount(address _owner, uint256 _amount) public onlyOwner returns (uint256) {
uint256 lockingAmount = locked[_owner].add(_amount);
require(balanceOf(_owner) >= lockingAmount, "Locking amount must not exceed balance");
locked[_owner] = lockingAmount;
touchedByLock.push(_owner);
emit Locked(_owner, lockingAmount);
return lockingAmount;
}
We should also update the required interface of the token inside the StoryDao contract to include this new function’s signature:
我们还应该更新StoryDao合约内令牌的必需接口，以包括此新功能的签名：
// ...
function getUnlockedAmount(address _owner) view public returns (uint256);
function unlockForAll() public;
}
With the active-story block we added before (inability to run certain functions unless the story’s active flag is true), this should do the trick. No one else will be able to waste money by sending it to the contract, and everyone’s tokens will get unlocked.
使用我们之前添加的active-story块(除非故事的active标志为true，否则无法运行某些功能)，这应该可以解决问题。 没有其他人能够通过将其发送到合同来浪费金钱，并且每个人的代币都将被解锁。
The owner doesn’t get the ether people submitted. Instead, the withdrawal function becomes available so people can take their ether back, and everyone’s taken care of.
所有者没有让以太人提交。 取而代之的是，提款功能变得可用，因此人们可以收回自己的以太币，所有人都得到了照顾。
Now our contracts are finally ready for deployment.
现在我们的合同终于可以部署了。
自我毁灭呢？ (What about selfdestruct?)
There’s a function called selfdestruct which makes it possible to destroy a contract. It looks like this:
有一个名为selfdestruct的函数，可以破坏合同。 看起来像这样：
selfdestruct(address);
Calling it will disable the contract in question, removing its code from the blockchain’s state and disabling all functions, all while sending the ether in that address to the address provided. This is not a good idea in our case: we still want people to be able to withdraw their ether; we don’t want to take it from them. Besides, any ether sent straight to the address of a suicided contract will get lost forever (burned) because there’s no way to get it back. 调用它将禁用有问题的合同，从区块链的状态中删除其代码并禁用所有功能，同时将该地址中的以太币发送到提供的地址。 在我们的情况下，这不是一个好主意：我们仍然希望人们能够撤出以太币； 我们不想从他们那里拿走它。 此外，任何直接送往自杀合同地址的以太币都将永远丢失(燃烧)，因为没有办法将其取回。
部署合同 (Deploying the Contract)
To deploy the smart contracts fully, we need to do the following:
要完全部署智能合约，我们需要执行以下操作：
deploy to mainnet 部署到主网
send tokens to StoryDAO address 将令牌发送到StoryDAO地址
transfer ownership of Token contract to StoryDao. 将代币合约的所有权转让给StoryDao。
Let’s go.
我们走吧。
主网部署 (Mainnet Deployment)
To deploy to mainnet, we need to add a new network into our truffle.js file:
要部署到主网，我们需要在truffle.js文件中添加一个新网络：
mainnet: {
provider: function() {
return new WalletProvider(
Wallet.fromPrivateKey(
Buffer.from(PRIVKEY, "hex")), "https://mainnet.infura.io/"+INFURAKEY
);
},
gasPrice: w3.utils.toWei("20", "gwei"),
network_id: "1",
},
Luckily, this is very simple. It’s virtually identical to the Rinkeby deployment; we just need to remove the gas amount (let it calculate it on its own) and change the gas price. We should also change the network ID to 1 since that’s the mainnet ID. 幸运的是，这很简单。 它实际上与Rinkeby部署完全相同。 我们只需要删除汽油量(让它自己计算)并更改汽油价格即可。 我们还应该将网络ID更改为1，因为那是主网ID。
We use this like so:
我们这样使用：
truffle migrate --network mainnet
There’s one caveat to note here. If you’re deploying on a network you previously deployed on (even if you just deployed the token onto the mainnet and wanted to deploy the StoryDao later) you might get this error:
这里要注意一个警告。 如果您要在先前部署的网络上进行部署(即使您只是将令牌部署到主网上并想稍后再部署StoryDao)，则可能会遇到以下错误：
Attempting to run transaction which calls a contract function, but recipient address XXX is not a contract address
This happens because Truffle remembers where it deployed already-deployed contracts so that it can reuse them in subsequent migrations, avoiding the need to re-deploy. But if your network restarted (i.e. Ganache) or you made some incompatible changes, it can happen that the address it has saved doesn’t actually contain this code any more, so it will complain. You can get around this by resetting migrations:
发生这种情况是因为Truffle记住了已部署合同的部署位置，以便它可以在以后的迁移中重用它们，而无需重新部署。 但是，如果您的网络重新启动(即Ganache)或进行了一些不兼容的更改，则可能发生的情况是它保存的地址实际上不再包含此代码，因此它将抱怨。 您可以通过重置迁移来解决此问题：
truffle migrate --network mainnet --reset
将令牌发送到StoryDao地址 (Send tokens to StoryDao address)
Get the address of the token and the address of the StoryDao from the deployment process.
从部署过程中获取令牌的地址和StoryDao的地址。
Then just use MEW as previously described to send the tokens.
然后，只需使用前面所述的MEW发送令牌即可。
If you get Out of Gas errors, just increase the gas limit. Remember: the rest of the unused gas always gets refunded, so there’s no fear of losing any more money than your transaction costs (sending the tokens should be under 40000 gas). 如果出现Out of Gas错误，只需增加气体限制。 请记住：其余未使用的汽油总是会退还的，因此不用担心会损失比您的交易成本更多的钱(发送代币的汽油价格应低于40000汽油)。
将令牌所有权转让给StoryDao (Transfer ownership of token to StoryDao)
To transfer ownership, we need to call the transferOwnership function on the token. Let’s load the token into MEW. In the Contracts screen, we enter the address and the contract’s ABI (grab it from the /build folder). Clicking Access will then let us access the functions in that contract in a menu from which we select transferOwnership.
要转让所有权，我们需要在令牌上调用transferOwnership函数。 让我们将令牌加载到MEW中。 在“ Contracts屏幕中，我们输入地址和合同的ABI(从/build文件夹中获取)。 单击“ 访问”将使我们可以在菜单中选择该合同，以选择transferOwnership 。
Tip: it’s enough to only include the ABI of the functions you intend to call: it doesn’t have to be the whole ABI of the token! You could just include the ABI of the transferOwnership function and it would be fine!
提示：仅包含要调用的函数的ABI就足够了：它不必是令牌的整个ABI！ 您可以只包含transferOwnership函数的ABI，就可以了！
We then select the new owner (the address of the deployed StoryDao) and unlock the current owner’s wallet (the same wallet we previously sent tokens from).
然后，我们选择新所有者(已部署StoryDao的地址)并解锁当前所有者的钱包(我们之前从中发送令牌的钱包)。
After writing this change, we can inspect the read-only function owner in the token contract (same menu as transferOwnership). It should show the new owner now.
写入此更改后，我们可以检查令牌合同中的只读函数owner (与transferOwnership相同的菜单)。 现在应该显示新所有者。
To make sure that the StoryDao address actually has the tokens, we can select the balanceOf function and enter the StoryDao’s address into the _owner field, then click on Read:
为了确保StoryDao地址确实具有令牌，我们可以选择balanceOf函数，然后在_owner字段中输入StoryDao的地址，然后单击Read ：
Indeed, 100 million tokens are in the StoryDao address.
实际上，StoryDao地址中有1亿枚代币。
Tip: we could have done the token sending and ownership transferring as part of the deployment step as well. Try figuring out how in a test environment.
提示：在部署步骤中，我们也可以完成令牌发送和所有权转移。 尝试弄清楚如何在测试环境中。
验证 (Verification)
As per part 3 of this series, it would benefit us greatly to verify the contracts of both the DAO and the Token on Etherscan. That green checkmark can go a long way.
根据本系列的第3部分 ，在Etherscan上验证DAO和令牌的合同将对我们大有裨益。 那个绿色的对勾可能会走很长一段路。
Follow the instructions in that part to get your contracts verified. Note that, during the verification step, you’ll now have to mark the optimizer as active, since we’re optimizing our code for cheaper deployment!
按照该部分中的说明进行合同验证。 请注意，在验证步骤中，您现在必须将优化器标记为活动状态，因为我们正在优化代码以实现更便宜的部署！
部署到网络 (Deploying to the Web)
To deploy the web UI of StoryDao, follow the instructions from the “regular” web development world. Since, in this case, it’s all static code, you can even host it on GitHub Pages or something similar.
要部署StoryDao的Web UI，请遵循“常规” Web开发领域的说明。 由于在这种情况下，它们都是静态代码，因此您甚至可以将其托管在GitHub Pages或类似的东西上。
Read about some of the options here.
在此处了解一些选项。
Once the page is up, configure the UI to use the addresses of contracts we got from the migration step. Alternatively, register ENS names for the token and the StoryDao, and you can keep the web UI static and fixed, hardcoding the addresses, and then only change the Ethereum address those ENS names are pointing at.
页面启动后，将UI配置为使用从迁移步骤获得的合同地址。 或者，注册令牌和StoryDao的ENS名称，然后可以保持Web UI固定和固定，对地址进行硬编码，然后仅更改这些ENS名称所指向的以太坊地址。
结论 (Conclusion)
This concludes the DAO tutorial. We hope it helped you recognize the complexities of Solidity development, but we also hope that it made some things clearer and made you more curious.
DAO教程到此结束。 我们希望它可以帮助您认识到Solidity开发的复杂性，但是我们也希望它可以使某些事情变得更清楚并使您更加好奇。
As always, the best way to learn is by doing. Experiment, make mistakes, reboot and rebuild. Blockchain development of this type is in high demand, and it’s only getting more popular, so you have an opportunity to get in on the ground floor.
一如既往，最好的学习方法就是边做边学。 实验，犯错误，重新启动并重建。 这种类型的区块链开发有很高的需求，并且越来越流行，因此您有机会进入底层。
Good luck!
祝好运！
P.S. If you enjoyed this tutorial, feel free to ping the author on Twitter. If you have complaints, suggestions, ideas, etc. please shoot them over into the repository on GitHub! PS：如果您喜欢本教程，请随时在Twitter上ping作者 。 如果您有任何投诉，建议，想法等，请将其发布到GitHub上的存储库中！ 翻译自: https://www.sitepoint.com/building-ethereum-dapps-launching-storydao/以太坊dapp
展开全文
• 以太坊dappIn part 5 of this tutorial series on building DApps with Ethereum, we dealt with adding content to the story, looking at how to add the ability for participants to buy tokens from the DAO ...
以太坊dappIn part 5 of this tutorial series on building DApps with Ethereum, we dealt with adding content to the story, looking at how to add the ability for participants to buy tokens from the DAO and to add submissions into the story. It’s now time for the DAO’s final form: voting, blacklisting/unblacklisting, and dividend distribution and withdrawal. We’ll throw in some additional helper functions for good measure.
在本教程系列中的第5部分中 ，我们介绍了如何在故事中添加内容，探讨如何增加参与者从DAO购买代币以及向故事中添加提交内容的能力。 现在是DAO最终形式的时候了：投票，将黑名单/取消黑名单以及股息分配和提款。 我们将添加一些额外的辅助函数以很好地解决问题。
If you get lost in all this, the full source code is available in the the repo.
如果您迷失了所有这些，则完整的源代码可在repo中找到 。
投票和提案 (Votes and Proposals)
We’ll be issuing Proposals and voting with Votes. We need two new structs:
我们将发布提案并通过投票进行投票。 我们需要两个新的结构：
struct Proposal {
string description;
bool executed;
int256 currentResult;
uint8 typeFlag; // 1 = delete
bytes32 target; // ID of the proposal target. I.e. flag 1, target XXXXXX (hash) means proposal to delete submissions[hash]
uint256 creationDate;
uint256 deadline;
mapping (address => bool) voters;
Vote[] votes;
address submitter;
}

Proposal[] public proposals;
uint256 proposalCount = 0;
event ProposalAdded(uint256 id, uint8 typeFlag, bytes32 hash, string description, address submitter);
event ProposalExecuted(uint256 id);
event Voted(address voter, bool vote, uint256 power, string justification);

struct Vote {
bool inSupport;
address voter;
string justification;
uint256 power;
}
A Proposal will have a mapping of voters to prevent people from voting on a proposal twice, and some other metadata which should be self-explanatory. The Vote will either be a yes or no vote, and will remember the voter along with their justification for voting a certain way, and the voting power — the number of tokens they want to devote to voting for this proposal. We also added an array of Proposals so we can store them somewhere, and a counter for counting how many proposals there are.
投标书将包含选民的映射，以防止人们对投标书进行两次投票，而其他一些元数据则应该是不言自明的。 投票将是“是”或“否”表决，并且将记住投票者及其以某种方式投票的理由，以及投票权-他们想要为该提案投票的代币数量。 我们还添加了一组提案，以便我们可以将它们存储在某个地方，以及一个用于计数有多少个提案的计数器。
Let’s build their accompanying functions now, starting with the voting function:
让我们现在从表决功能开始构建它们的附带功能：
modifier tokenHoldersOnly() {
require(token.balanceOf(msg.sender) >= 10**token.decimals());
_;
}

function vote(uint256 _proposalId, bool _vote, string _description, uint256 _votePower) tokenHoldersOnly public returns (int256) {

require(_votePower > 0, "At least some power must be given to the vote.");
require(uint256(_votePower) <= token.balanceOf(msg.sender), "Voter must have enough tokens to cover the power cost.");

Proposal storage p = proposals[_proposalId];

require(p.executed == false, "Proposal must not have been executed already.");
require(p.deadline > now, "Proposal must not have expired.");
require(p.voters[msg.sender] == false, "User must not have already voted.");

uint256 voteid = p.votes.length++;
Vote storage pvote = p.votes[voteid];
pvote.inSupport = _vote;
pvote.justification = _description;
pvote.voter = msg.sender;
pvote.power = _votePower;

p.voters[msg.sender] = true;

p.currentResult = (_vote) ? p.currentResult + int256(_votePower) : p.currentResult - int256(_votePower);
token.increaseLockedAmount(msg.sender, _votePower);

emit Voted(msg.sender, _vote, _votePower, _description);
return p.currentResult;
}
Notice the function modifier: by adding that modifier into our contract, we can attach it to any future function and make sure only token holders can execute that function. It’s a reusable security check!
注意函数修饰符：通过将修饰符添加到我们的合约中，我们可以将其附加到任何将来的函数上，并确保只有令牌持有者才能执行该函数。 这是可重复使用的安全检查！
The vote function does some sanity checks like the voting power being positive, the voter having enough tokens to actually vote etc. Then we fetch the proposal from storage and make sure it’s neither expired nor already executed. It wouldn’t make sense to vote on a proposal that’s already done. We also need to make sure this person hasn’t yet voted. We could allow changing the vote power, but this opens the DAO to some vulnerabilities like people withdrawing their votes at the last minute etc. Perhaps a candidate for a future version?
投票功能会进行一些健全性检查，例如投票权是否为正，投票者是否具有足够的令牌进行实际投票等。然后，我们从存储中提取提案，并确保提案未过期或尚未执行。 对已经完成的提案进行投票是没有意义的。 我们还需要确保此人尚未投票。 我们可以允许更改投票权，但这会使DAO面临一些漏洞，例如人们在最后一刻撤回投票等。也许是未来版本的候选人？
Then we register a new Vote into the proposal, change the current result for easy lookup of scores, and finally emit the Voted event. But what’s token.increaseLockedAmount?
然后，我们将新的投票注册到提案中，更改当前结果以轻松查找分数，最后发出Voted事件。 但是token.increaseLockedAmount什么？
This bit of logic increases the amount of locked tokens for a user. The function is only executable by the owner of the token contract (by this point that’s hopefully the DAO) and will prevent the user from sending an amount of tokens that exceeds the locked amount registered to their account. This lock is lifted after the proposal falls through or executes.
此逻辑位增加了用户的锁定令牌数量。 该功能仅可由令牌合约的所有者执行(至此，希望是DAO)，并且将阻止用户发送超过注册到其帐户的锁定数量的令牌数量。 提案失败或执行后，此锁将解除。
Let’s write the functions for proposing the deletion of an entry now.
让我们现在编写建议删除条目的函数。
投票删除和列入黑名单 (Voting to Delete and Blacklist)
As established in part 1 in this series, we have three entry deletion functions planned:
如本系列第1部分中所述，我们计划了三个条目删除功能：
Remove entry: when confirmed by vote, the target entry is removed. Voting time: 48 hours. 删除条目 ：经投票确认后，将删除目标条目。 投票时间： 48小时 。 Emergency remove entry [Only Owner]: can only be triggered by Owner. When confirmed by vote, the target entry is removed. Voting time: 24 hours. 紧急删除条目[仅所有者] ：只能由所有者触发。 经投票确认后，将删除目标条目。 投票时间： 24小时 。 Emergency remove image [Only Owner]: only applies to image entries. Can only be triggered by Owner. When confirmed by vote, the target entry is removed. Voting time: 4 hours. 紧急删除图像[仅所有者] ：仅适用于图像条目。 只能由所有者触发。 经投票确认后，将删除目标条目。 投票时间： 4小时 。 Five deletions of a single address’ entries lead to a blacklisting.
对单个地址条目的五个删除会导致黑名单。
Let’s see how we can do that now. First up, the deletion functions: 让我们看看我们现在该如何做。 首先，删除功能：
modifier memberOnly() {
require(whitelist[msg.sender]);
require(!blacklist[msg.sender]);
_;
}

function proposeDeletion(bytes32 _hash, string _description) memberOnly public {

require(submissionExists(_hash), "Submission must exist to be deletable");

uint256 proposalId = proposals.length++;
Proposal storage p = proposals[proposalId];
p.description = _description;
p.executed = false;
p.creationDate = now;
p.submitter = msg.sender;
p.typeFlag = 1;
p.target = _hash;

p.deadline = now + 2 days;

emit ProposalAdded(proposalId, 1, _hash, _description, msg.sender);
proposalCount = proposalId + 1;
}

function proposeDeletionUrgent(bytes32 _hash, string _description) onlyOwner public {

require(submissionExists(_hash), "Submission must exist to be deletable");

uint256 proposalId = proposals.length++;
Proposal storage p = proposals[proposalId];
p.description = _description;
p.executed = false;
p.creationDate = now;
p.submitter = msg.sender;
p.typeFlag = 1;
p.target = _hash;

p.deadline = now + 12 hours;

emit ProposalAdded(proposalId, 1, _hash, _description, msg.sender);
proposalCount = proposalId + 1;
}

function proposeDeletionUrgentImage(bytes32 _hash, string _description) onlyOwner public {

require(submissions[_hash].image == true, "Submission must be existing image");

uint256 proposalId = proposals.length++;
Proposal storage p = proposals[proposalId];
p.description = _description;
p.executed = false;
p.creationDate = now;
p.submitter = msg.sender;
p.typeFlag = 1;
p.target = _hash;

p.deadline = now + 4 hours;

emit ProposalAdded(proposalId, 1, _hash, _description, msg.sender);
proposalCount = proposalId + 1;
}
Once proposed, a Proposal is added to the list of proposals and notes which entry is being targeted by the entry hash. The description is saved and some defaults added, and a deadline is calculated depending on proposal type. The proposal added event gets emitted and the total number of proposals is increased.
一旦提议，就将提议添加到提议列表，并注意条目哈希所针对的条目。 将保存描述并添加一些默认值，并根据投标类型计算截止日期。 发出添加提案的事件，并且提案总数增加。
Next let’s see how to execute a proposal. To be executable, a proposal must have enough votes, and must be past its deadline. The execution function will accept the ID of the proposal to execute. There’s no easy way to make the EVM execute all pending proposals at once. It’s possible that too many would be pending to execute and that they would make big changes to the data in the DAO, which might exceed the gas limit of Ethereum blocks, thereby failing the transaction. It’s much easier to build a manual execution function callable by anyone with well defined rules, so the community can keep an eye on the proposals that need executing.
接下来，让我们看看如何执行提案。 要执行，提案必须具有足够的投票权，并且必须超过其截止日期。 执行功能将接受要执行的提案的ID。 没有简单的方法可以使EVM立即执行所有待处理的投标。 可能有太多待执行的事务，它们将对DAO中的数据进行重大更改，这可能会超出以太坊块的限制，从而导致交易失败。 构建手动执行功能可以使任何人使用明确定义的规则调用起来要容易得多，因此社区可以关注需要执行的提案。
function executeProposal(uint256 _id) public {
Proposal storage p = proposals[_id];
require(now >= p.deadline && !p.executed);

if (p.typeFlag == 1 && p.currentResult > 0) {
assert(deleteSubmission(p.target));
}

uint256 len = p.votes.length;
for (uint i = 0; i < len; i++) {
token.decreaseLockedAmount(p.votes[i].voter, p.votes[i].power);
}

p.executed = true;
emit ProposalExecuted(_id);
}
We grab the proposal by its ID, check that it meets requirements of not having been executed and deadline being expired, and then if the type of the proposal is a deletion proposal and the voting result is positive, we use the already written deletion function, finally emitting a new event we added (add it to the top of the contract). The assert call is there serving the same purpose as the require statement: assert is generally used when you “assert” that a result is true. Require is used for prerequisites. Functionally they’re identical, with the difference of assert statements not being able to accept message parameters for cases when they fail. The function ends by unlocking the tokens for all the votes in that one proposal.
我们通过提案ID来获取提案，检查其是否符合未执行且截止日期已过期的要求，然后，如果提案的类型为删除提案且投票结果为肯定，则使用已经写入的删除功能，最后发出一个我们添加的新事件(将其添加到合同的顶部)。 assert调用在那里与require语句具有相同的目的：断言通常在您“断言”结果为真时使用。 Require用于先决条件。 从功能上讲，它们是相同的，不同之处在于assert语句在失败时无法接受消息参数。 该功能通过解锁该提案中所有投票的令牌来结束。
We can use this same approach to add other types of proposals, but first, let’s update the deleteSubmission function to ban the users that have five or more deletions on their account: it means they’ve been consistently submitting content the community voted against. Let’s update the deleteSubmission function: 我们可以使用相同的方法来添加其他类型的建议，但是首先，让我们更新deleteSubmission函数，以禁止其帐户中删除了五个或更多内容的用户：这意味着他们一直在提交社区投票反对的内容。 让我们更新deleteSubmission函数：
function deleteSubmission(bytes32 hash) internal returns (bool) {
require(submissionExists(hash), "Submission must exist to be deletable.");
Submission storage sub = submissions[hash];

sub.exists = false;
deletions[submissions[hash].submitter] += 1;
if (deletions[submissions[hash].submitter] >= 5) {
blacklistAddress(submissions[hash].submitter);
}

emit SubmissionDeleted(
sub.index,
sub.content,
sub.image,
sub.submitter
);

nonDeletedSubmissions -= 1;
return true;
}
That’s better. Auto-blacklisting on five deletes. It wouldn’t be fair not to give the blacklisted addresses a chance to redeem themselves, though. We also need to define the blacklisting function itself. Let’s do both of those things and set the unblacklisting fee to, for example, 0.05 ether.
这样更好 在五个删除后自动列入黑名单。 但是，不给黑名单中的地址一个赎回自己的机会是不公平的。 我们还需要定义黑名单功能本身。 让我们做这两个事情，并将取消列入黑名单的费用设置为例如0.05乙醚。
function blacklistAddress(address _offender) internal {
require(blacklist[_offender] == false, "Can't blacklist a blacklisted user :/");
blacklist[_offender] == true;
token.increaseLockedAmount(_offender, token.getUnlockedAmount(_offender));
emit Blacklisted(_offender, true);
}

function unblacklistMe() payable public {
unblacklistAddress(msg.sender);
}

function unblacklistAddress(address _offender) payable public {
require(msg.value >= 0.05 ether, "Unblacklisting fee");
require(blacklist[_offender] == true, "Can't unblacklist a non-blacklisted user :/");
require(notVoting(_offender), "Offender must not be involved in a vote.");
withdrawableByOwner = withdrawableByOwner.add(msg.value);
blacklist[_offender] = false;
token.decreaseLockedAmount(_offender, token.balanceOf(_offender));
emit Blacklisted(_offender, false);
}

function notVoting(address _voter) internal view returns (bool) {
for (uint256 i = 0; i < proposalCount; i++) {
if (proposals[i].executed == false && proposals[i].voters[_voter] == true) {
return false;
}
}
return true;
}
Notice that a blacklisted account’s tokens get locked up until they send in the unblacklisting fee.
请注意，列入黑名单的帐户的令牌将被锁定，直到他们发送取消列入黑名单的费用为止。
其他类型的投票 (Other Types of Votes)
Using the inspiration from the functions we wrote above, try writing the other proposals. For spoilers, check out the GitHub repo of the project and copy the final code from there. For brevity, let’s move on to the other functions we still have left in the DAO.
利用我们上面编写的功能的启发，尝试编写其他建议。 对于破坏者，请查看项目的GitHub存储库 ，然后从那里复制最终代码。 为简便起见，让我们继续我们仍留在DAO中的其他功能。
章末 (Chapter End)
Once the time or chapter limit of the story is reached, it’s time to bring the story to an end. Anyone can call the ending function after the date which will allow withdrawals of dividends. First, we need a new StoryDAO attribute and an event:
一旦达到故事的时间或篇章限制，就可以结束故事了。 任何人都可以在允许提取股息的日期之后调用结束函数。 首先，我们需要一个新的StoryDAO属性和一个事件：
bool public active = true;
event StoryEnded();
Then, let’s build the function:
然后，让我们构建函数：
function endStory() storyActive external {
withdrawToOwner();
active = false;
emit StoryEnded();
}
Simple: it deactivates the story after sending the collected fees to the owner and emits the event. But in actuality, this doesn’t really change anything in the DAO as a whole: the other functions don’t react to it being over. So let’s build another modifier:
简单：在将收取的费用发送给所有者并发出事件后，它便停用了故事。 但是实际上，这实际上并没有真正改变整个DAO中的任何内容：其他功能对其结束没有任何React。 因此，让我们构建另一个修饰符：
modifier storyActive() {
require(active == true);
_;
}
Then, we add this modifier to all the functions except withdrawToOwner, like so:
然后，将此修饰符添加到除withdrawToOwner之外的所有函数中，如下所示：
function whitelistAddress(address _add) storyActive public payable {
In case any tokens are left over in the DAO, let’s take them back and take over ownership of these tokens in order to be able to use them on another story later on:
如果DAO中遗留了任何令牌，那么让我们收回它们并接管这些令牌的所有权，以便以后可以在另一个故事中使用它们：
function withdrawLeftoverTokens() external onlyOwner {
require(active == false);
token.transfer(msg.sender, token.balanceOf(address(this)));
token.transferOwnership(msg.sender);
}

function unlockMyTokens() external {
require(active == false);
require(token.getLockedAmount(msg.sender) > 0);

token.decreaseLockedAmount(msg.sender, token.getLockedAmount(msg.sender));
}
The unlockMyTokens function is there to unlock all locked tokens in case some stayed locked for a specific user. It shouldn’t happen, and this function should be removed by a good amount of tests.
如果某些锁对于特定用户保持锁定状态，那么unlockMyTokens函数可用于解锁所有锁定的令牌。 这不应该发生，并且应该通过大量测试来删除此功能。
股利分配和提取 (Dividend Distribution and Withdrawals)
Now that the story has ended, the fees collected for submissions need to be distributed to all token holders. We can re-use our whitelist to mark everyone who’s made the withdrawal of the fees:
现在故事已经结束，提交的费用需要分配给所有代币持有者。 我们可以重新使用白名单来标记所有撤回费用的人：
function withdrawDividend() memberOnly external {
require(active == false);
uint256 owed = address(this).balance.div(whitelistedNumber);
msg.sender.transfer(owed);
whitelist[msg.sender] = false;
whitelistedNumber--;
}
If these dividends aren’t withdrawn within a certain time limit, the owner can grab the rest:
如果未在一定期限内提取这些股息，则所有者可以收取其余款项：
function withdrawEverythingPostDeadline() external onlyOwner {
require(active == false);
require(now > deadline + 14 days);
owner.transfer(address(this).balance);
}
For homework, consider how easy or hard it would be to re-use this same deployed smart contract, clear its data, and keep the tokens in the pot and restart another chapter from this without re-deploying. Try doing this yourself and keep an eye on the repo for future updates to the tutorial series covering this! Also think about additional incentive mechanics: maybe the amount of tokens in an account influences the dividend they get from the story’s end? Your imagination is the limit!
对于家庭作业，请考虑重用相同的已部署智能合约，清除其数据并将令牌保留在锅中并从中重新开始下一章而无需重新部署会多么容易或困难。 尝试自己执行此操作，并注意回购，以获取有关此内容的教程系列的将来更新！ 还要考虑其他激励机制：也许一个账户中的代币数量会影响他们从故事的结尾获得的股息？ 您的想象力是极限！
部署问题 (Deployment Issues)
Given that our contract is quite large now, deploying and/or testing it might exceed the gas limit of Ethereum blocks. This is what limits large applications from being deployed on the Ethereum network. To get it deployed anyway, try using the code optimizer during compilation by changing the truffle.js file to include solc settings for optimization, like so:
鉴于我们的合同现在很大，部署和/或测试它可能超出以太坊区块的天然气限制。 这就是限制大型应用程序部署在以太坊网络上的原因。 要无论如何部署它，请尝试在编译期间使用代码优化器，方法是将truffle.js文件更改为包括用于优化的solc设置，如下所示：
// ...

module.exports = {
solc: {
optimizer: {
enabled: true,
runs: 200
}
},
networks: {
development: {
// ...
This will run the optimizer across the code 200 times to find areas that can be minified, removed or abstracted before deployment, which should reduce the deployment cost significantly.
这将使优化程序在整个代码中运行200次，以发现可以在部署之前缩小，删除或抽象的区域，这将大大降低部署成本。
结论 (Conclusion)
This concludes our exhaustive DAO development — but the course isn’t over yet! We still have to build and deploy the UI for this story. Luckily, with the back end completely hosted on the blockchain, building the front end is much less complicated. Let’s look at that in our penultimate part of this series. 这结束了我们详尽的DAO开发-但课程尚未结束！ 我们仍然需要构建和部署该故事的UI。 幸运的是，由于后端完全托管在区块链上，因此构建前端要简单得多。 让我们在本系列的倒数第二部分中看一下。 翻译自: https://www.sitepoint.com/building-ethereum-dapps-voting-custom-tokens/以太坊dapp
展开全文
• 以太坊DApp开发环境搭建-附件资源
• 以太坊dappIn part 2 of this tutorial series on building DApps with Ethereum, we wrote the TNS token’s code. But we haven’t yet compiled it, deployed it, tested it or verified it. Let’s do all that ...
以太坊dappIn part 2 of this tutorial series on building DApps with Ethereum, we wrote the TNS token’s code. But we haven’t yet compiled it, deployed it, tested it or verified it. Let’s do all that in this part so that we’re ready for what comes next.
在本教程系列的第2部分中 ，我们使用Ethereum构建DApp，我们编写了TNS令牌的代码。 但是我们尚未对其进行编译，部署，测试或验证。 让我们完成本部分中的所有操作，以便为接下来的工作做好准备。
编译中 (Compiling)
At this point we have a file containing some Solidity code. But to make the Ethereum Virtual Machine understand it, we need to turn it into machine code. Additionally, in order to communicate with it from a web application, we need an ABI (application binary interface), which is a universally readable description of the functions that exist in a certain smart contract — be it a token or something more complex. We can create machine code for the EVM and the ABI all at once by using Truffle’s compiler.
至此，我们有了一个包含一些Solidity代码的文件。 但是要使以太坊虚拟机理解它，我们需要将其转换为机器代码。 另外，为了从Web应用程序与之通信，我们需要一个ABI(应用程序二进制接口)，它是对某种智能合约中存在的功能的通用可读描述-是令牌还是更复杂的东西。 我们可以使用Truffle的编译器一次为EVM和ABI创建机器代码。
In the project folder, run:
在项目文件夹中，运行：
truffle compile
This command will look inside the contracts subfolder, compile them all and place their compiled version into the build subfolder. Note that if you used the alternative development flow from the last part, all the parent contracts from which our TNSToken contract is inheriting functionality will also be compiled one by one each in its own file.
该命令将查看contracts子文件夹内部，将其全部编译，然后将其编译版本放入build子文件夹。 请注意，如果您使用了最后一部分的替代开发流程，那么我们的TNSToken合同继承其功能的所有父合同也将在其自己的文件中一个一个地编译。
Feel free to inspect the contents of the generated JSON files. Our TNSToken should have over 10000 lines of JSON code.
随时检查生成的JSON文件的内容。 我们的TNSToken应该包含10000行以上的JSON代码。
部署到Ganache (Deploying to Ganache)
Now let’s see if we can deploy this to our simulated Ganache blockchain. If Ganache isn’t running already in a tab of your terminal or among your operating system’s applications, run it with:
现在让我们看看是否可以将其部署到模拟的Ganache区块链中。 如果Ganache尚未在终端的选项卡中或操作系统的应用程序中运行，请使用以下命令运行它：
ganache-cli
Or run the app to get a screen like this one:
或运行该应用以获取如下所示的屏幕：
Then, back in the folder where we just compiled the contracts, we have to add a migration. Create the file migrations/2_deploy_tnstoken.js. If you’re not familiar with migrations in the Truffle ecosystem, see this guide.
然后，回到我们刚刚编制合同的文件夹中，我们必须添加一个迁移。 创建文件migrations/2_deploy_tnstoken.js 。 如果您不熟悉Truffle生态系统中的迁移，请参阅本指南 。
Let’s put the following into that file:
让我们将以下内容放入该文件：
var Migrations = artifacts.require("./Migrations.sol");
var TNSToken = artifacts.require("./TNSToken.sol");

module.exports = function(deployer, network, accounts) {
deployer.deploy(TNSToken, {from: accounts[0]});
};
First the ability to do migrations at all is imported by requesting Migrations.sol. This is required in every migration. Next, deploying a token means we need to import its Solidity code, and we do this by pulling in TNSToken.sol, the code we wrote in the previous part. Finally, this is cookie cutter migrating with only the part between function(deployer, network, accounts) { and the last } changing.
首先，通过请求Migrations.sol来导入进行Migrations.sol 。 每次迁移都需要这样做。 接下来，部署令牌意味着我们需要导入其Solidity代码，并通过TNSToken.sol (我们在上一部分中编写的代码)来实现此目的。 最后，这是千篇一律的小工具，仅在function(deployer, network, accounts) {和最后一个}之间的部分进行迁移。
In this case, we tell the deployer to deploy the TNSToken and pass in the from argument in order to set the initial token holder. The address used here is a random one generated by Ganache, but by using the accounts array automatically passed to the deployer, we make sure we have access to the list of accounts present in the running node (be it a live Geth node or Ganache). In my particular example, the account[0] address was 0xdFb659D556d926dd3585d0f18d4F8eD7939E8061, also evident in the screenshot above. 在这种情况下，我们告诉部署者部署TNSToken并传入from参数，以设置初始令牌持有者。 此处使用的地址是由Ganache生成的随机地址，但是通过使用自动传递给部署程序的accounts数组，我们确保可以访问正在运行的节点(实时的Geth节点或Ganache)中存在的帐户列表。 。 在我的特定示例中， account[0]地址为0xdFb659D556d926dd3585d0f18d4F8eD7939E8061 ，这在上面的屏幕截图中也很明显。
Let’s also not forget to configure a development environment in truffle.js:
我们也不要忘记在truffle.js配置开发环境：
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*"
}
}
};
Note: take care of the port and IP; yours might be different!
注意：注意端口和IP； 您的可能会有所不同！
Finally, in the project folder, run truffle migrate. You should see something like this:
最后，在项目文件夹中，运行truffle migrate 。 您应该会看到以下内容：
Notice the Ethereum address next to TNStoken: 0x3134bcded93e810e1025ee814e87eff252cff422. This is where our token was deployed. Now let’s see it in action.
注意复仇地址旁边TNStoken ： 0x3134bcded93e810e1025ee814e87eff252cff422 。 这是我们的令牌的部署位置。 现在让我们来看一下它的作用。
测试令牌 (Testing the Token)
Automated tests are not necessary in this case. Token contracts are highly standardized and battle tested. If we had used some functionality that goes beyond the scope of a traditional token, then automated tests would have come in handy. As it is, though, testing it by sending it around to and from addresses is quite enough.
在这种情况下，不需要自动测试。 代币合同是高度标准化的，并且经过了战役测试。 如果我们使用的功能超出了传统令牌的范围，那么自动化测试将派上用场。 不过，通过在地址之间来回发送进行测试就足够了。
Open a wallet UI like MyEtherWallet and in the top right menu select a custom network. In the dialog, enter the information given to you by your private blockchain — either Ganache or an actual PoA blockchain, whichever you’re running based on part 1 of this tutorial series. In my example, that’s 127.0.0.1 for the address, and 7545 for the port.
打开类似MyEtherWallet的钱包用户界面，然后在右上角的菜单中选择一个自定义网络。 在对话框中，输入您的私人区块链(Ganache或实际的PoA区块链)提供给您的信息，无论您运行的是基于本教程系列的第1部分 。 在我的示例中，地址为127.0.0.1 ，端口为7545 。
Open the wallet which you set as the from value in the deployment script. If you’re using Ganache, you’ll see its private key printed on screen in the Ganache UI or the ganache output in the terminal.
打开您在部署脚本中设置为from值的钱包。 如果您使用的是Ganache，您将在Ganache UI或终端上的ganache输出中看到其私钥打印在屏幕上。
Finally, MEW needs to be told that this token exists. We do this by adding a custom token.
最后，需要告知MEW该令牌存在。 我们通过添加自定义标记来实现。
Immediately after having added the token, you’ll notice that the account now has a balance of 100 million of them, and that it’s able to send them in the currency dropdown selection menu. Let’s try sending some to another address. 添加令牌后，您会立即注意到该帐户现在有1亿美元的余额，并且能够在货币下拉选择菜单中发送它们。 让我们尝试发送一些到另一个地址。
Go ahead and send those back now to get the original account back to 100 million again. We’ve just made sure our token’s basic functionality works as intended.
继续并立即将其退回，以使原始帐户再次回到1亿美元。 我们只是确保了令牌的基本功能按预期工作。
部署到实时网络 (Deploying to a Live Network)
This wouldn’t be a real token test without also deploying it on a live network. Let’s not use the mainnet, though, but a testnet like Rinkeby.
如果不将其部署在实时网络中，这将不是真正的令牌测试。 但是，我们不要使用主网，而要使用像Rinkeby这样的测试网。
In truffle.js, let’s add a new network — rinkeby — so that our file looks like this:
在truffle.js ，让我们添加一个新网络rinkeby ，以便我们的文件如下所示：
require('dotenv').config();
const WalletProvider = require("truffle-wallet-provider");
const Wallet = require('ethereumjs-wallet');
const Web3 = require("web3");
const w3 = new Web3();

const PRIVKEY = process.env["PRIVKEY"];
const INFURAKEY = process.env["INFURAKEY"];

module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*"
},
rinkeby: {
provider: function() {
return new WalletProvider(
Wallet.fromPrivateKey(
Buffer.from(PRIVKEY, "hex")), "https://rinkeby.infura.io/"+INFURAKEY

);
},
gas: 4600000,
gasPrice: w3.utils.toWei("50", "gwei"),
network_id: "3",
},
}
};
Yikes! What is all this now?
kes！ 现在这是什么？
Let’s process it line by line.
让我们逐行处理它。
The first few lines import some node modules so we can use the functions below. If you get a missing module message for any of those, just running npm install web3-wallet-provider truffle-wallet-provider web3 dotenv --save should fix things.
前几行导入了一些节点模块，因此我们可以使用以下功能。 如果您发现其中任何一个缺少模块消息，只需运行npm install web3-wallet-provider truffle-wallet-provider web3 dotenv --save解决问题。
Next, we load the private key of the wallet from which we’re running the contract (so the wallet which will be getting the 100 million tokens; we can’t use the from value here) from a .env file in the root of the project folder. If it doesn’t exist, create it. That same file also has an Infura.io access key, which is a website that hosts Ethereum nodes and lets apps connect to them so developers don’t need to run full Ethereum nodes on their computers.
接下来，我们从根目录下的.env文件中加载运行合同的钱包的私钥(因此该钱包将获得1亿个代币；此处不能使用from值)。项目文件夹。 如果不存在，请创建它。 该文件还具有Infura.io访问密钥，该访问密钥是托管以太坊节点并允许应用程序连接到它们的网站，因此开发人员无需在其计算机上运行完整的以太坊节点。
The .env file is hidden by default and can be ignored in .gitignore so there’s no danger of your private key ever leaking — a very important precaution! This is what the file contains:
默认情况下， .env文件是隐藏的，可以在.gitignore忽略。因此，您的私钥永远不会泄漏，这是非常重要的预防措施！ 这是文件包含的内容：
PRIVKEY="YOUR_PRIVATE_KEY";
INFURAKEY="YOUR_INFURA_ACCESS_KEY";
You can get your Infura key by registering here. The private key can easily be obtained if you just install Metamask, switch it to Rinkeby, and go to Export Private Key. Any method will work, though, so choose whatever you like. You can also use Ganache’s private key. A single private key unlocks the same wallet on all Ethereum networks — testnet, rinkeby, mainnet, you name it.
您可以在此处注册获取Infura密钥。 如果仅安装Metamask ，将其切换到Rinkeby，然后转到Export Private Key，则可以轻松获得私钥。 任何方法都可以，但是，请选择您喜欢的任何方法。 您还可以使用Ganache的私钥。 单个私钥可以在所有以太坊网络(测试网，rinkeby，主网，您命名)上解锁相同的钱包。
Back to our config file. We have a new network entry: rinkeby. This is the name of the Ethereum testnet we’ll be deploying to and the code inside the provider is basically cookie-cutter copy paste telling Truffle “grab my private key, hex-encode it, make it into an unlocked wallet, and then talk to Infura through it”. 回到我们的配置文件。 我们有一个新的network条目： rinkeby 。 这是我们将要部署到的以太坊测试网的名称，提供程序内部的代码基本上是用小甜饼复制粘贴告诉Truffle“抓住我的私钥，对其进行十六进制编码，使其放入解锁的钱包中，然后再进行通话通过Infura”。
Lastly, we define the gas limit we want to spend on executing this contract (4.6 million is enough, which can be changed if needed), how much the gas will cost (50 Gwei is actually quite expensive, but the Ether we’re playing with is simulated so it doesn’t matter) and the network ID is set to 4 because that’s how the Rinkeby testnet is labeled.
最后，我们定义了要在执行该合同上花费的瓦斯限制(460万就足够了，可以根据需要更改)，瓦斯将花费多少(50 Gwei实际上非常昂贵 ，但是我们正在玩的以太坊和一起模拟，因此没有关系)，并且网络ID设置为4，因为这就是Rinkeby测试网标记的方式 。
There’s one more thing we need to do. The migration file we wrote earlier is targeting a from address, but the address for Rinkeby is different. Does this mean we need to change the deployment script depending on the network? Of course not! Let’s change the 2_deploy_tnstoken.js file to look like this:
我们还需要做一件事。 我们前面写的迁移文件的目标是一个from地址，但该地址Rinkeby是不同的。 这是否意味着我们需要根据网络来更改部署脚本？ 当然不是！ 让我们将2_deploy_tnstoken.js文件更改为如下所示：
var Migrations = artifacts.require("./Migrations.sol");
var TNSToken = artifacts.require("./TNSToken.sol");

module.exports = function(deployer, network, accounts) {
if (network == "development") {
deployer.deploy(TNSToken, {from: accounts[0});
} else {
deployer.deploy(TNSToken);
}
};
As you can see, deployment scripts are simple JavaScript and the second parameter given to the deployer is always the network’s name — which is what we can use to differentiate between them.
如您所见，部署脚本是简单JavaScript，分配给部署者的第二个参数始终是网络的名称，这是我们可以用来区分它们的名称。
If we try to run the migration now with truffle migrate --network rinkeby, it will fail if the address we used is new:
如果我们现在尝试使用truffle migrate --network rinkeby运行迁移，则如果我们使用的地址是新地址，它将失败：
This is because the address has no Ether to spend on deploying the contract. That’s easy to solve, though. Just head over to the Rinkeby Faucet and ask for some. It’s free.
这是因为该地址没有以太坊可用于部署合同。 不过，这很容易解决。 只需前往Rinkeby水龙头并索取一些。 免费。
Now re-run the migration and the token contract will be deployed live on the Rinkeby network. It can be tested just like in the Ganache use case above. Everything should work exactly the same, only now you can also test it with your friends and colleagues. Progress!
现在，重新运行迁移，令牌合同将在Rinkeby网络上实时部署。 可以像上面的Ganache用例一样对其进行测试。 一切都应该完全相同，只是现在您还可以与朋友和同事进行测试。 进展！
奖励：验证和ENS (Bonus: Verification and ENS)
For extra trust points, it’s recommended that you verify the token on Etherscan and register an ENS domain for it.
为了获得额外的信任点，建议您在Etherscan上验证令牌并为其注册ENS域。
验证 (Verification)
Verification means submitting the source code of the token to Etherscan so it can compare it to what’s deployed on the network, thereby verifying it as backdoor-free. This is done on the Code tab of the token’s address. Since our token uses some third-party code and those can’t be easily pulled into the code window in the Verify screen, we need to flatten our contract. For that, we’ll use a tool called truffle-flattener:
验证意味着将令牌的源代码提交给Etherscan，以便可以将其与网络上部署的令牌进行比较，从而将其验证为无后门程序。 这是在令牌地址的“代码”选项卡上完成的。 由于我们的令牌使用了一些第三方代码，而这些代码无法轻易地插入到“ 验证”屏幕的代码窗口中，因此我们需要弄平合同。 为此，我们将使用一个名为truffle-flattener的工具：
npm install --global truffle-flattener
This tool will copy all the dependencies and the token’s source code into a single file. We can run it like this:
该工具会将所有依赖项和令牌的源代码复制到一个文件中。 我们可以这样运行它：
truffle-flattener contracts/TNSToken.sol >> ./contracts/TNSTokenFlat.sol
A new file should be present in the contracts folder now, virtually identical to our source code but with dependency code pasted in (so SafeMath.sol, for example, will be pasted at the top of the file).
现在应该在contracts文件夹中出现一个新文件，该文件实际上与我们的源代码相同，但是粘贴了相关性代码(例如， SafeMath.sol将粘贴在文件顶部)。
Paste the contents of that new file into the code window of the Verify screen, set the compiler to whatever version you get by running truffle version, and set Optimization to No. Click Verify and Publish, and once the process completes, your token’s address screen will have new tabs: Read Contract and Write Contract, and the Code tab will have a green checkmark, indicating that the code has been verified. This gives you additional trust points with the community.
将该新文件的内容粘贴到“ 验证”屏幕的代码窗口中，通过运行truffle version将编译器设置为所获得的任何truffle version ，并将“ 优化”设置为“ 否” 。 点击验证并发布 ，过程完成后，令牌的地址屏幕将具有新的标签： 读取合同和写入合同 ，并且代码标签将带有绿色的选中标记，表明代码已通过验证。 这为您提供了与社区的其他信任点。
ENS (ENS)
The ENS is the Ethereum Name System. It’s used to give Ethereum addresses human-readable names so you don’t have to remember the 0xmumbojumbo strings and can instead remember addresses like bitfalls.eth. You can then even register subdomains like token.bitfalls.eth. The process of registering an ENS domain is not simple and it takes time, so if you’d like to do that I recommend you read this guide and follow the instructions here.
ENS是以太坊名称系统。 它用于为以太坊地址提供人类可读的名称，因此您不必记住0xmumbojumbo字符串，而可以记住诸如bitfalls.eth地址。 然后，您甚至可以注册子域，例如token.bitfalls.eth 。 注册ENS域的过程并不简单，而且需要时间，因此，如果您愿意这样做，建议您阅读本指南并按照此处的说明进行操作。
结论 (Conclusion)
In this part we went through compiling and deploying a custom token. This token is compatible with all exchanges and can be used as a regular ERC20 token. 在这一部分中，我们经历了编译和部署自定义令牌的过程。 该令牌与所有交易所兼容，可以用作常规ERC20令牌。 翻译自: https://www.sitepoint.com/ethereum-dapps-compiling-deploying-testing-tns-tokens/以太坊dapp
展开全文
• 以太坊DApp高薪实战第四期
• 以太坊DApp开发实战 文档详见 以太坊DApp开发实战 全书目录： 第一章、以太坊入门基础篇： 课程介绍 以太坊基础知识 Windows搭建以太坊 Mac平台搭建以太坊 控制台模拟转账 第二章、智能合约开发篇： Remix开发...
• 汇智网 以太坊DApp开发-Geth私链环境搭建-Windows 平台 导读 本电子书由汇智网 创作适用于Windows 平台(Win7/Win10) 下以太坊 DApp 开发环境的搭建 汇智网推出了在线交互式以太坊 DApp 实戓开发课程以去中心化投票...
• 以太坊DApp开发环境搭建，自带区块链的一个小Demo，希望对大家学习区块链有帮助
• 以太坊DApp高薪实战第三期-直播回放
• 以太坊DApp高薪实战第二期-直播回放
• 以太坊DApp高薪实战第一期-直播回放
• 如何使用 Vue.js 开发以太坊 DApp Vue 是一套在前端开发中广泛采用的用于构建用户界面的渐进式 JavaScript 框架。Vue 通过响应的数据绑定和组合的视图组件让界面开发变得非常的简单。这边文章来看看如何使用 Vue ...
• 以太坊dappIn part 3 of this tutorial series on building DApps with Ethereum, we built and deployed our token to the Ethereum testnet Rinkeby. In this part, we’ll start writing the Story DAO code. 在...
• 以太坊是一个平台，它上面提供各种模块让用户来搭建应用，如果将搭建应用比作造房子，那么以太坊就提供了墙面、屋顶、地板等模块，用户只需像搭积木一样把房子搭起来，因此在以太坊上建立应用的成本和速度都大大改善...
• 本电子书适用于乌班图（Ubuntu）平台下以太坊DApp 开发环境的搭建。
• 以太坊dapp是什么In the cryptoverse, a lot of attention is laid on Bitcoin. But don't let that overshadow the growing interest in Ethereum, which is revolutionizing the way we think of applications. 在...
• 从零入门以太坊DAPP开发实战视频教程，自2017年起，众多基于以太坊开发的DAPP项目层出不穷，以CryptoKitties（迷恋猫）为首的应用受到广泛欢迎。如何基于以太坊开发DAPP？该视频课程会分享DAPP的基本框架、开发工具...
• 本文档，适用于Windows 平台 (Win7/Win10 )下以太坊 DApp 开发环境的搭建 。
• 以太坊dapp的样板。 开始使用 克隆此仓库。 安装并设置 chrome扩展程序。 在Kovan测试网络上配置MetaMask。 运行yarn安装依赖项，然后用yarn start启动开发服务器。 其他脚本 yarn run prettify -将漂亮应用于...
• Ubuntu下用Geth搭建私链环境，以及搭建以太坊DApp开发环境。
• 本指南将带您了解“ hello world”以太坊dapp的性质，用法和部署过程。 在开始之前，请在后台启动以下安装过程，以便在我们需要使用它时就可以准备就绪： 安装说明 OSX 首先，我们需要自制软件。 ruby -e "\$...
• Casino-dapp 使用web3.js和Vue.js的以太坊dapp的简单赌场
• Truffle以太坊Dapp开发框架 文章地址： 前言 区块链的开发对于大多数的人来说，都是一件很新、很难的事情，有太多不一样的技术要学习。区块链有自己的设计理念，不同于传统分布式系统架构，数据同步机制，共识算法等...
• 概念验证的以太坊dApp，用于发布社交活动和聚会。 在Rinkeby测试网上。 演示版 建筑 发展历程 启动testrpc客户端 npm run testrpc 监视并编译客户端 cd client/ npm run watch 运行客户端服务器 cd client/ npm ...

...