{"content":{"title":"如何使用 Dapptools | 类似 MakerDAO 使用的代码","body":"> * 原文链接：https://medium.com/@patrick.collins_58673/how-to-use-dapptools-code-like-makerdao-fed9909d055b\r\n> * 译文出自：[登链翻译计划](https://github.com/lbc-team/Pioneer)\r\n> * 译者：[Meta](https://learnblockchain.cn/people/5578)\r\n> * 校对：[Tiny熊](https://learnblockchain.cn/people/15)\r\n> * 本文永久链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/4675)\r\n\r\n\r\n\r\n了解如何使用[Dapptools](https://dapp.tools/)，这是一个智能合约部署框架，适用于喜欢 bash 和命令行的 web3 开发人员。我们着眼于使用它端到端的学习区块链部署框架。\r\n\r\n![How to use dapptools](https://img.learnblockchain.cn/attachments/2022/08/AoZwUH6J62eb8d435164c.png!/scale/50)\r\n\r\n\r\n[MakerDAO](https://makerdao.com/en/) 是目前规模最大的DeFi协议之一，其中[DAI](https://www.coingecko.com/en/coins/dai)稳定币是行业中应用最广泛的稳定币之一。他们的团队使用一种名为 [dapptools](https://dapp.tools/) 的特殊框架来创建、部署、测试智能合约，并与之交互。\r\n\r\ndapptools框架由[Dapphub](https://github.com/dapphub) 团队创建，是一个极简的bash友好工具，任何Linux高级用户都很容易爱上它，而且很多人已经爱上了它。\r\n\r\n![How to use Dapptools](https://img.learnblockchain.cn/attachments/2022/08/0irGOMQa62eb8d8fe0a54.png!/scale/50)\r\n\r\n[Transmissions11](https://twitter.com/transmissions11/status/1437518450880966656) 对 dapptools 兴奋不已 \r\n\r\n它对初学者也非常友好，所以如果这是你第一次了解部署框架，那么你来对地方了。在本文中，将展示如何使用 dapptools 执行以下操作：\r\n\r\n1. 编写和编译合约\r\n2. 使用solidity和fuzzing测试合约\r\n3. 部署合约\r\n4. 与已部署的合约交互\r\n\r\n将使用我们设置的 [dapptools-demo](https://github.com/PatrickAlphaC/dapptools-demo)来了解它。你可以随意跳到那里。如果需要，你还可以查看 [Foundry](https://github.com/gakonst/foundry)工具，它是 dapptools 的重写版本，但由[Paradigm](https://www.paradigm.xyz/) 团队用 rust 编写。\r\n\r\n要获得包含更多优秀代码和示例的完整存储库，请查看 [dapptools-starter-kit](https://github.com/smartcontractkit/dapptools-starter-kit)，它包含使用[Chainlink](https://chain.link/)的代码示例！\r\n\r\n如果你只想 git 克隆存储库以开始使用它，请随时遵循存储库中的自述文件！\r\n\r\n关于这一切的视频很快就会出来：\r\n\r\nhttps://www.youtube.com/watch?v=ZurrDzuurQs\r\n\r\nDapptools 视频\r\n\r\n## 项目设置\r\n\r\n### 开发环境\r\n\r\n首先，你需要一个代码编辑器，我是[VSCode](https://code.visualstudio.com/)的忠实粉丝。如果你使用的是Windows，则需要下载 [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)，因为我们将运行许多 Windows 命令。\r\n\r\n一旦你使用了 VSCode，[打开一个终端](https://code.visualstudio.com/docs/editor/integrated-terminal)来运行安装命令，或以任何通常运行 shell 命令的方式。\r\n\r\n#### 安装 / 要求\r\n\r\n1. Git\r\n2. Make\r\n3. Dapptools\r\n\r\n首先，你需要[安装git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)，按照该链接安装git。如果你可以运行，你就会知道做对了：\r\n\r\n```\r\ngit --version \r\n```\r\n\r\n然后，你需要确保已`make`安装。大多数计算机都已经安装了它，但如果没有，请查看有关该主题的[stack exchange](https://askubuntu.com/questions/161104/how-do-i-install-make) 问题。 \r\n\r\n然后，安装dapptools。一定要去[官方文档](https://github.com/dapphub/dapptools#installation) 安装，但它看起来像运行这个：\r\n\r\n```bash\r\n# user must be in sudoers\r\ncurl -L https://nixos.org/nix/install | sh\r\n\r\n# Run this or login again to use Nix\r\n. \"$HOME/.nix-profile/etc/profile.d/nix.sh\"\r\n\r\ncurl https://dapp.tools/install | sh\r\n```\r\n\r\n你应该有`dapp` , `seth` , `ethsign` , `hevm`和其他一些你现在可以运行的命令了！\r\n\r\n这些说明仅适用于基于 Unix 的系统（例如，MacOS, Linux)。\r\n\r\n## 创建本地 dapptools 项目\r\n\r\n要创建一个新文件夹，请运行以下命令：\r\n\r\n```\r\ndapp init\r\n```\r\n\r\n这将为你提供应如下所示的基本文件布局：\r\n\r\n```\r\n.\r\n├── Makefile\r\n├── lib\r\n│   └── ds-test\r\n│       ├── LICENSE\r\n│       ├── Makefile\r\n│       ├── default.nix\r\n│       ├── demo\r\n│       │   └── demo.sol\r\n│       └── src\r\n│           └── test.sol\r\n├── out\r\n│   └── dapp.sol.json\r\n└── src\r\n    ├── DapptoolsDemo.sol\r\n    └── DapptoolsDemo.t.sol\r\n```\r\n\r\n`Makefile`: 放置“脚本”的地方。Dapptools是基于命令行的，makefile可以帮助我们用几个字符运行大型命令。\r\n\r\n`lib`: 该文件夹用于外部依赖项，如[Openzeppelin](https://openzeppelin.com/contracts/) 或 [ds-test](https://github.com/dapphub/ds-test)。\r\n\r\n`out`: 编译代码的位置。类似于`brownie`中的`build`文件夹或`hardhat`中的`artifacts`文件夹。\r\n\r\n`src`: 你的智能合约就在这里。类似于`brownie`和`hardhat`中的`contracts`文件夹。\r\n\r\n## 运行测试\r\n\r\n要运行测试，你只需要运行：\r\n\r\n```\r\ndapp test\r\n```\r\n\r\n你会看到如下输出：\r\n\r\n```\r\nRunning 2 tests for src/DapptoolsDemo.t.sol:DapptoolsDemoTest\r\n[PASS] test_basic_sanity() (gas: 190)\r\n[PASS] testFail_basic_sanity() (gas: 2355)\r\n```\r\n\r\n## 模糊测试\r\n\r\nDapptools在[模糊测试](https://en.wikipedia.org/wiki/Fuzzing)上内置了一个重点。这是一个非常强大的工具，可以用随机数据测试我们的合约。\r\n\r\n让我们用一个名为`play`的函数来更新`DapptoolsDemo.sol`。我们的新文件应该是这样的:\r\n\r\n```solidity\r\n// SPDX-License-Identifier: GPL-3.0-or-later\r\npragma solidity ^0.8.6;\r\n\r\ncontract DapptoolsDemo {\r\n\r\nfunction play(uint8 password) public pure returns(bool){\r\n        if(password == 55){\r\n            return false;\r\n        }\r\n        return true;\r\n    }\r\n}\r\n```\r\n\r\n我们将在`DappToolsDemo.t.sol`中添加一个新的测试，该测试兼容模糊测试名为`test_basic_fuzzing`。 这个文件看起来像这样：\r\n\r\n```solidity\r\n// SPDX-License-Identifier: GPL-3.0-or-later\r\npragma solidity ^0.8.6;\r\n\r\nimport \"ds-test/test.sol\";\r\n\r\nimport \"./DapptoolsDemo.sol\";\r\n\r\ncontract DapptoolsDemoTest is DSTest {\r\n    DapptoolsDemo demo;\r\n    \r\nfunction setUp() public {\r\n        demo = new DapptoolsDemo();\r\n    }\r\n    \r\nfunction testFail_basic_sanity() public {\r\n        assertTrue(false);\r\n    }\r\n    \r\nfunction test_basic_sanity() public {\r\n        assertTrue(true);\r\n    }\r\n    \r\nfunction test_basic_fuzzing(uint8 value) public {\r\n        bool response = demo.play(value);\r\n        assertTrue(response);\r\n    }\r\n}\r\n```\r\n\r\n现在可以给我们的合约提供随机数据，如果我们的代码给它一个数字`55`，我们就会期望它出错。用模糊标志运行测试:\r\n\r\n```\r\ndapp test — fuzz-runs 1000\r\n```\r\n\r\n将看到如下输出：\r\n\r\n```\r\n+ dapp clean\r\n+ rm -rf out\r\nRunning 3 tests for src/DapptoolsDemo.t.sol:DapptoolsDemoTest\r\n[PASS] test_basic_sanity() (gas: 190)\r\n[PASS] testFail_basic_sanity() (gas: 2355)\r\n[FAIL] test_basic_fuzzing(uint8). Counterexample: (55)\r\nRun:\r\n dapp test --replay '(\"test_basic_fuzzing(uint8)\",\"0x0000000000000000000000000000000000000000000000000000000000000037\")'\r\nto test this case again, or \r\n dapp debug --replay '(\"test_basic_fuzzing(uint8)\",\"0x0000000000000000000000000000000000000000000000000000000000000037\")'\r\nto debug it.\r\n\r\nFailure: \r\n  \r\n  Error: Assertion Failed\r\n```\r\n\r\n我们的模糊测试发现了异常值！我为`test_basic_fuzzing`测试运行了`1000`条不同的路径，并找到了`55`这个异常值。 这对于找到那些你可能没有想到的破坏合约的随机用例非常重要。\r\n\r\n## 从 Openzeppelin 和外部合约导入\r\n\r\n假设我们想使用 Openzeppelin 标准创建一个 NFT。可以使用`dapp install`命令安装外部合约或包。 需要命名GitHub存储库组织和要安装的存储库名称。\r\n\r\n首先，我们需要提交到目前为止的更改！Dapptools 将外部包作为[git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules)引入，因此我们需要先提交。\r\n\r\n运行:\r\n\r\n```\r\ngit add .\r\n\r\ngit commit -m ‘initial commit’\r\n```\r\n\r\n然后，我们可以安装我们的外部包。例如，对于 [OpenZeppelin,](https://github.com/OpenZeppelin/openzeppelin-contracts,)，我们将使用：\r\n\r\n```\r\ndapp install OpenZeppelin/openzeppelin-contracts\r\n```\r\n\r\n你应该会在lib文件夹中看到一个名为`openzeppelin-contracts`的新文件夹。\r\n\r\n## **NFT合约**\r\n\r\n在src文件夹中创建一个名为NFT.sol的新文件。然后添加以下代码:\r\n\r\n```solidity\r\n// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\nimport \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\r\n\r\ncontract NFT is ERC721 {\r\n    uint256 public tokenCounter;\r\n    constructor () ERC721 (\"NFT\", \"NFT\"){\r\n        tokenCounter = 0;\r\n    }\r\n\r\nfunction createCollectible() public returns (uint256) {\r\n        uint256 newItemId = tokenCounter;\r\n        _safeMint(msg.sender, newItemId);\r\n        tokenCounter = tokenCounter + 1;\r\n        return newItemId;\r\n    }\r\n    \r\n}\r\n```\r\n\r\n如果你现在尝试`dapp build`，你会得到一个很大的错误！\r\n\r\n## **重新映射**\r\n\r\n我们需要告诉 dapptools`import “@openzeppelin/contracts/token/ERC721/ERC721.sol”;`指向我们的`lib`文件夹。所以我们创建了一个名为 `remappings.txt` 的文件并添加：\r\n\r\n```\r\n@openzeppelin/=lib/openzeppelin-contracts/\r\nds-test/=lib/ds-test/src/\r\n```\r\n\r\n然后，我们创建一个名为`.dapprc`的文件并添加以下行：\r\n\r\n```\r\nexport DAPP_REMAPPINGS=$(cat remappings.txt)\r\n```\r\n\r\nDapptools在我们的`.dapprc`中查找不同的配置变量，有点像hardhat中的`hardhat.config.js`。在这个配置文件中，我们告诉它读取输出`remappings.txt`并将其用作“重新映射”。重新映射是我们在solidity中告诉导入的文件应该从哪里导入的方法。例如在`remapping.txt`我们看到：\r\n\r\n```\r\n@openzeppelin/=lib/openzeppelin-contracts/\r\n```\r\n\r\n这意味着我们告诉dapptools，当它编译一个文件时，如果它在import语句中看到`@openzeppelin/`，它应该在`lib/openzeppelin-contracts/`中查找文件。 所以如果我们这样做\r\n\r\n```\r\nimport \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\r\n```\r\n\r\n我们实际上是在说:\r\n\r\n```\r\nimport \"lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol\";\r\n```\r\n\r\n然后，为了不编译整个库，我们需要将以下代码添加到`.dapprc`文件中:\r\n\r\n```\r\nexport DAPP_LINK_TEST_LIBRARIES=0\r\n```\r\n\r\n这告诉dapptools在运行测试时不要在lib中编译所有内容。\r\n\r\n## 部署到测试网（如果需要，也可以部署到主网……）\r\n\r\n> 注意：如果你想设置自己的本地网络，可以运行`dapp testnet`。\r\n\r\n\r\n\r\n## 将.env添加到.gitignore文件中\r\n\r\n如果你还没有，请创建一个`.gitignore`文件，然后在其中添加这一行：\r\n\r\n```\r\n.env\r\n```\r\n\r\n请这样做。在本教程中，我们根本不会把你的私钥推送到git中，但我们希望养成将其添加到`.gitignore`中的习惯！这将有助于防止你不小心将环境变量发送到公共git仓库。你仍然可以强迫他们，所以要小心！\r\n\r\n## 设置`ETH_RPC_URL`环境变量\r\n\r\n要部署到测试网，我们需要一个区块链节点。 [Alchemy](https://alchemy.com/?a=673c802981) 项目是个不错的选择。你可以获得免费的测试网 HTTP 端点。只需注册一个免费项目，然后点击`view key`（或当时的任何文本），你将拥有一个 HTTP 端点！\r\n\r\n你可以选择你喜欢的测试网，我会从[Chainlink Faucets](https://faucets.chain.link/)中选择一个，可以在其中获得测试网 LINK 和 ETH。Kovan或Rinkeby将会是很好的选择，所以无论哪一个都可以。\r\n\r\n如果还没有，请创建一个`.env`文件，然后将端点添加到`.env`文件中。它看起来像这样:\r\n\r\n```\r\nexport ETH_RPC_URL=http://alchemy.io/adfsasdfasdf\r\n```\r\n\r\n## 创建默认发送方\r\n\r\n获得一个[eth wallet](https://metamask.io/)，如果你还没有的话。你可以在[这里](https://docs.chain.link/docs/deploy-your-first-contract/#install-and-fund-your-metamask-wallet)看到关于设置metamask的更深入的说明。但理想情况下，你得到一个metamask，然后从 [Chainlink Faucets](https://faucets.chain.link/)水龙头得到一些测试网ETH。然后切换到你正在使用的测试网。你的metamask应该看起来像这样:\r\n\r\n![Dapptools tutorial | Metamask](https://img.learnblockchain.cn/attachments/2022/08/TVsc1rUQ62eb9749210a4.png)\r\n\r\n[Metamask](https://metamask.io/) \r\n\r\n拥有钱包后，将该钱包的地址设置为`ETH_FROM`环境变量。\r\n\r\n```\r\nexport ETH_FROM=YOUR_ETH_WALLET_ADDRESS\r\n```\r\n\r\n此外，如果使用 Kovan，[请使用测试网 ETH 为你的钱包注资](https://faucets.chain.link/)。\r\n\r\n## 添加你的私钥\r\n\r\n> 注意:我强烈推荐使用一个没有任何真正资金的metamask来开发。\r\n\r\n> 如果你将你的私钥推送到一个包含真钱的公共仓库，人们就可以窃取你的资金。\r\n\r\n因此，如果你刚刚设置了metamask，并且只使用测试网资金，那么你是安全的。 😃\r\n\r\nDapptools附带了一个名为`ethsign`的工具，这是我们将要存储和加密密钥的地方。要添加我们的私钥(需要发送交易)，请获取你的钱包的私钥，并运行:\r\n\r\n```\r\nethsign import\r\n```\r\n\r\n然后它会提示你添加你的私钥，然后是加密的密码。这将在`ethsign`中加密你的私钥。． 任何时候你想发送交易，都需要你的密码。如果你运行命令`ethsign ls`，会得到这样的响应:\r\n\r\n```\r\n0x3DF02ac6fEe39B79654AA81C6573732439e73A81 keystore\r\n```\r\n\r\n你做对了。\r\n\r\n## 更新你的Makefile\r\n\r\n可以使用`dapp create DapptoolsDemo`命令来部署合约，然后添加一些标志到环境变量中。为了让生活更简单，可以将部署命令添加到Makefile中，并告诉Makefile使用我们的环境变量。\r\n\r\n将以下内容添加到`Makefile`中\r\n\r\n```\r\n-include .env\r\n```\r\n\r\n## 部署合约\r\n\r\n在`Makefile`中，有一个名为 `deploy`的命令，它将运行`dapp create DapptoolsDemo`并包含我们的环境变量。要运行它，只需运行：\r\n\r\n```\r\nmake deploy\r\n```\r\n\r\n\r\n\r\n系统将提示你输入密码。一旦成功，它将部署你的合约!\r\n\r\n```\r\ndapp create DapptoolsDemo\r\n++ seth send --create 608060405234801561001057600080fd5b50610158806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806353a04b0514610030575b600080fd5b61004a60048036038101906100459190610096565b610060565b60405161005791906100d2565b60405180910390f35b600060378260ff161415610077576000905061007c565b600190505b919050565b6000813590506100908161010b565b92915050565b6000602082840312156100ac576100ab610106565b5b60006100ba84828501610081565b91505092915050565b6100cc816100ed565b82525050565b60006020820190506100e760008301846100c3565b92915050565b60008115159050919050565b600060ff82169050919050565b600080fd5b610114816100f9565b811461011f57600080fd5b5056fea264697066735822122004d7143940853a7650f1383002b6ba56991e7a5c7d763e755774a149ca0465e364736f6c63430008060033 'DapptoolsDemo()'\r\nseth-send: warning: `ETH_GAS' not set; using default gas amount\r\nEthereum account passphrase (not echoed): seth-send: Published transaction with 376 bytes of calldata.\r\nseth-send: 0xeb871eee1fa31c34583b63002e2b16a0252410b5615623fd254b1f90b67369d4\r\nseth-send: Waiting for transaction receipt........\r\nseth-send: Transaction included in block 29253678.\r\n0xC5a62934B912c3B1948Ab0f309e31a9b8Ed08dd1\r\n```\r\n\r\n你应该能够看到[Etherscan](https://kovan.etherscan.io/address/0xC5a62934B912c3B1948Ab0f309e31a9b8Ed08dd1)上给出的最终地址。\r\n\r\n## 与合约交互\r\n\r\n要与已部署的合约交互，我们可以使用`seth call`和`seth send`，它们略有不同：\r\n\r\n- `seth call` : 只会从区块链读取数据。它不会“消耗”任何[gas](https://www.sofi.com/learn/content/what-is-ethereum-gas/)。\r\n- `seth send` : 这会将交易发送到区块链，可能会修改区块链的状态，并消耗gas。\r\n\r\n要从区块链中**读取**数据，可以执行以下操作：\r\n\r\n```\r\nETH_RPC_URL=<YOUR_RPC_URL> seth call <YOUR_DEPLOYED_CONTRACT> \r\n\"FUNCTION_NAME()\" <ARGUMENTS_SEPARATED_BY_SPACE>\r\n```\r\n\r\n例如:\r\n\r\n```\r\nETH_RPC_URL=<YOUR_RPC_URL> seth call 0x12345 \"play(uint8)\" 55\r\n```\r\n\r\n得到的结果是`0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000`，这意味着false，因为响应等于0，而在布尔类型中，0表示false。\r\n\r\n要将数据**写入**到区块链，可以执行以下操作：\r\n\r\n```\r\nETH_RPC_URL=<YOUR_RPC_URL> ETH_FROM=<YOUR_FROM_ADDRESS> seth send \r\n<YOUR_DEPLOYED_CONTRACT> \"FUNCTION_NAME()\" \r\n<ARGUMENTS_SEPARATED_BY_SPACE>\r\n```\r\n\r\n我们没有部署一个有很好例子的合约，但是假设该`play`函数*能够*修改区块链状态，看起来像：\r\n\r\n```\r\nETH_RPC_URL=<YOUR_RPC_URL> ETH_FROM=<YOUR_FROM_ADDRESS> seth send \r\n0x12345 \"play(uint8)\" 55\r\n```\r\n\r\n## 在 Etherscan 上验证你的合约\r\n\r\n将合约部署到 etherscan 后，可以通过以下方式对其进行验证：\r\n\r\n1. 获取[Etherscan API 密钥](https://etherscan.io/apis)。\r\n\r\n2. 然后运行\r\n\r\n```\r\nETHERSCAN_API_KEY=<api-key> dapp verify-contract \r\n<contract_directory>/<contract>:<contract_name> <contract_address>\r\n```\r\n\r\n例如:\r\n\r\n```\r\nETHERSCAN_API_KEY=123456765 dapp verify-contract ./src/DapptoolsDemo.sol:DapptoolsDemo 0x23456534212536435424\r\n```\r\n\r\n## 最后\r\n\r\n1. 添加`cache`到你的`.gitignore`\r\n\r\n2. 添加`update:; dapp update` 到 `Makefile`的顶部。当你运行 `make` 时，将更新并下载`.gitmodules`和`lib`中的文件。\r\n\r\n3. 添加一个`LICENSE`。如果你不知道怎么做，可以从[我们的仓库](https://github.com/PatrickAlphaC/dapptools-demo)中复制一个！\r\n\r\n终于大功告成！\r\n\r\n# **资源**\r\n\r\n如果你喜欢这个，考虑捐赠！\r\n\r\n💸 ETH 钱包地址: 0x9680201d9c93d65a3603d2088d125e955c73BD65\r\n\r\n- [Dapptools](https://dapp.tools/)\r\n- [Hevm Docs](https://github.com/dapphub/dapptools/blob/master/src/hevm/README.md)\r\n- [Dapp Docs](https://github.com/dapphub/dapptools/tree/master/src/dapp/README.md)\r\n- [Seth Docs](https://github.com/dapphub/dapptools/tree/master/src/seth/README.md)"},"author":{"user":"https://learnblockchain.cn/people/5578","address":null},"history":"QmUMGeg9i1Dk29iAHgCHoZuKcG2WBw85rKW1RTX3QWmpeN","timestamp":1669774071,"version":1}