使用Dapp工具构建和部署托管智能合约

  • QuickNode
  • 发布于 2024-12-30 23:51
  • 阅读 30

本文提供了一份详尽的指南,介绍了如何使用 Dapp Tools 开发、测试和部署一个基本的Escrow智能合约。内容涵盖了所需的系统环境、工具安装、合约创建及测试,最后讲解了如何在 Sepolia 测试网上进行部署和交互,适合希望提升以太坊开发技能的读者。

info

请注意,dapp.tools 目前不再积极开发,这可能会导致兼容性问题或破坏性更改。Foundry 作为一个良好维护的替代方案,为寻求持续支持和更新的用户提供服务。请查看我们相关的 Foundry 指南 这里

概述

准备好提升你的以太坊开发技能了吗?在本指南中,我们将深入探讨 Dapp Tools 以构建、测试和部署一个基本的 Escrow 智能合约。我们将探索 Dapp Tools 的高级功能,这些功能可以简化你的开发工作流程。开始吧!

我们将做什么

  • 了解 Dapp Tools
  • 设置 Dapp Tools
  • 创建一个基本的 Escrow 智能合约
  • 使用 Dapp Tools 编写和运行测试
  • 将 Escrow 合约部署到 Sepolia 测试网
  • 与已部署合约进行交互

你将需要什么

  • 一台 Linux 或 Windows 机器
  • 已安装 Nix
  • 已在系统上安装 Dapp Tools
  • 对以太坊和智能合约有基本理解
  • 一个 QuickNode 端点(你可以在 这里 创建一个)
  • 一个有一些 Sepolia ETH 的以太坊钱包(你可以从 Multi-Chain QuickNode faucet 获取一些)
依赖项 版本
nix 2.24.9
dapp 0.35.0
seth 0.12.0

项目先决条件:创建一个 QuickNode 端点

你需要一个 API 端点来与以太坊网络通信。你可以使用公共节点或部署并管理自己的基础设施;不过,如果你希望获得 8 倍的响应速度,可以将这些重担留给我们。请在 这里 注册一个免费账户。

登录后,点击 创建端点,然后选择 Ethereum Sepolia 测试网链。

QuickNode Endpoint

创建端点后,复制 HTTP Provider URL 链接并保持备用,因为你在开发 Escrow 智能合约时将需要它。

项目先决条件:为你的钱包提供资金

在将我们的 Escrow 合约部署到 Sepolia 测试网之前,我们需要确保有足够的 Sepolia 测试网 ETH 来支付 gas 费用。 Multi-Chain QuickNode Faucet 提供了一个轻松获取测试 ETH 的方式。获取你的 Sepolia 测试网 ETH 的步骤如下:

  1. 访问 Multi-Chain QuickNode Faucet
  2. 连接你的钱包(例如,MetaMask、Coinbase Wallet)或者粘贴你的钱包地址。
  3. 从网络选项中选择 Sepolia。
  4. 请求你的测试 ETH。

Multi-Chain QuickNode Faucet

注意:在以太坊主网络上使用 EVM faucet 时,主网余额要求为 0.001 ETH。你还可以通过推特或者登录你的 QuickNode 账户来获得额外奖励!

收到你的 Sepolia 测试网 ETH 后,你就可以开始设置项目文件夹。

Dapp Hub 的 Dapp Tools 是什么?

Dapp Tools 是 Dapp Hub 为以太坊智能合约开发创建的一组命令行工具。它帮助开发者更高效地构建、测试和部署智能合约。

Dapp Tools 的主要组件包括:

  1. dapp:管理智能合约项目,包括构建、测试和部署。
  2. seth:用于区块链交互的以太坊命令行客户端。
  3. hevm:一个 EVM 调试器和符号执行引擎,用于高级测试。
  4. ethsign:使用 JSON 密钥库或硬件钱包签署以太坊交易。

Dapp Tools 还包括一些有用的智能合约库:

  • ds-test:一个 Solidity 单元测试框架。
  • ds-math:为整数和定点数字提供安全数学操作。
  • ds-auth:在智能合约中处理访问控制。

文档概述可以在 这里 找到。

这些工具协同工作,为开发人员更有效地创建和测试智能合约提供帮助。Dapp Tools 提供了基于属性的测试、形式验证和 gas 优化等功能,这可以导致更可靠的智能合约。

接下来,让我们开始设置我们的 Dapp Tools 项目。

设置 Dapp Tools

info

在安装 Dapp Tools 之前,请确保你已安装 Nix。Nix 是一个为类 Unix 系统设计的包管理器和构建系统,帮助使包管理更加可靠和可重现。

首先,让我们安装 Dapp Tools。打开你的终端并运行:

nix profile install github:dapphub/dapptools#{dapp,ethsign,hevm,seth} --extra-experimental-features nix-command --extra-experimental-features flakes

你应该看到 Dapp Tools 的版本号显示。

创建 Escrow 智能合约

让我们为我们的 Escrow 合约创建一个新的 Dapp 项目:

mkdir escrow-contract
cd escrow-contract
dapp init

接下来,让我们创建我们的 Escrow 合约。在代码编辑器中打开 src/EscrowContract.sol 文件并添加以下代码:

pragma solidity ^0.8.6;

contract Escrow {
    address public admin;
    mapping(uint256 => address) public participantIds;
    mapping(address => uint256) public participantIdsByAddress;
    mapping(address => uint256) public balances;
    mapping(address => bool) public whitelist;
    uint256 public participantCount;

    event Deposit(address indexed participant, uint256 amount);
    event Distribution(uint256 indexed participantId, address indexed participant, uint256 amount);
    event Whitelisted(address indexed participant, uint256 participantId);
    event RemovedFromWhitelist(address indexed participant);
    event BatchWhitelisted(uint256 count);
    event EmergencyWithdraw(address indexed admin, uint256 amount);

    constructor() {
        admin = msg.sender;
    }

    modifier onlyAdmin() {
        require(msg.sender == admin, "Only admin can perform this action");
        _;
    }

    modifier onlyWhitelisted() {
        require(whitelist[msg.sender], "Address not whitelisted");
        _;
    }
    function addParticipants(address[] memory _participants) public onlyAdmin {
        for (uint i = 0; i < _participants.length; i++) {
            if (!whitelist[_participants[i]]) {
                participantCount++;
                whitelist[_participants[i]] = true;
                participantIds[participantCount] = _participants[i];
                participantIdsByAddress[_participants[i]] = participantCount;
                emit Whitelisted(_participants[i], participantCount);
            }
        }
        emit BatchWhitelisted(_participants.length);
    }

    function removeFromWhitelist(address _participant) public onlyAdmin {
        require(whitelist[_participant], "Address not whitelisted");
        whitelist[_participant] = false;
        emit RemovedFromWhitelist(_participant);
    }

    function depositFunds() public payable onlyWhitelisted {
        require(msg.value > 0, "Deposit amount must be greater than 0");
        balances[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }
    function distribute(uint256 _participantId, uint256 _amount) public onlyAdmin {
        address payable participant = payable(participantIds[_participantId]);
        require(participant != address(0), "Invalid participant ID");
        require(_amount > 0, "Distribution amount must be greater than 0");
        require(_amount <= address(this).balance, "Insufficient contract balance");

        (bool success, ) = participant.call{value: _amount}("");
        require(success, "Transfer failed");
        emit Distribution(_participantId, participant, _amount);
    }

    function emergencyWithdraw() public onlyAdmin {
        uint256 balance = address(this).balance;
        require(balance > 0, "No funds to withdraw");

        (bool success, ) = payable(admin).call{value: balance}("");
        require(success, "Transfer failed");
        emit EmergencyWithdraw(admin, balance);
    }

    function getParticipant(uint256 _participantId) public view returns (address) {
        return participantIds[_participantId];
    }

    function getParticipantId(address _participant) public view returns (uint256) {
        return participantIdsByAddress[_participant];
    }

    function getBalance(address _participant) public view returns (uint256) {
        return balances[_participant];
    }

    function getContractBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

这个 Escrow 合约提供了以下核心功能:

  1. 白名单管理:管理员可以添加或移除参与者的白名单。
  2. 存款:白名单上的参与者可以将资金存入合约。
  3. 分配:管理员可以使用独特的 ID 将资金分配给特定的参与者。
  4. 紧急提款:管理员在紧急情况下可以从合约中提取所有资金。
  5. 余额追踪:合约可以跟踪每个参与者的余额和合约的总余额。

使用 Dapp Tools 编写测试

Dapp Tools 使用 ds-test 库来编写测试。让我们为我们的 Escrow 合约创建测试。在文件 src/EscrowContract.t.sol 中输入以下代码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

import "ds-test/test.sol";
import "./EscrowContract.sol";

contract EscrowTest is DSTest {
    Escrow escrow;
    address admin;
    address alice = address(0x1);
    address bob = address(0x2);
    address charlie = address(0x3);

    function setUp() public {
        admin = address(this);
        escrow = new Escrow();
    }

    function testAddParticipants() public {
        address[] memory addresses = new address[](3);
        addresses[0] = alice;
        addresses[1] = bob;
        addresses[2] = charlie;

        escrow.addParticipants(addresses);

        assertTrue(escrow.whitelist(alice));
        assertTrue(escrow.whitelist(bob));
        assertTrue(escrow.whitelist(charlie));
        assertEq(escrow.getParticipantId(alice), 1);
        assertEq(escrow.getParticipantId(bob), 2);
        assertEq(escrow.getParticipantId(charlie), 3);
    }

    function testDeposit() public {
        address[] memory addresses = new address[](1);
        addresses[0] = address(this);
        escrow.addParticipants(addresses);

        escrow.depositFunds{value: 1 ether}();
        assertEq(escrow.getBalance(address(this)), 1 ether);
        assertEq(escrow.getContractBalance(), 1 ether);
    }

    function testFailDepositNotWhitelisted() public {
        escrow.depositFunds{value: 1 ether}();
    }

    function testDistribute() public {
        address[] memory addresses = new address[](1);
        addresses[0] = alice;
        escrow.addParticipants(addresses);

        addresses[0] = address(this);
        escrow.addParticipants(addresses);
        escrow.depositFunds{value: 2 ether}();

        uint256 initialBalance = alice.balance;
        escrow.distribute(1, 1 ether);
        assertEq(alice.balance, initialBalance + 1 ether);
        assertEq(escrow.getContractBalance(), 1 ether);
    }

    function testFailDistributeInvalidId() public {
        escrow.distribute(999, 1 ether);
    }

    function testEmergencyWithdraw() public {
        address[] memory addresses = new address[](1);
        addresses[0] = address(this);
        escrow.addParticipants(addresses);
        escrow.depositFunds{value: 5 ether}();

        uint256 initialBalance = address(this).balance;
        escrow.emergencyWithdraw();
        assertEq(address(this).balance, initialBalance + 5 ether);
        assertEq(escrow.getContractBalance(), 0);
    }

    receive() external payable {}
}

此测试文件包括以下测试用例:

  1. 将参与者添加到白名单
  2. 作为白名单参与者存款
  3. 尝试作为非白名单参与者存款(应失败)
  4. 将资金分配给特定参与者
  5. 尝试使用无效参与者 ID 进行分配(应失败)
  6. 作为管理员进行紧急提款

在执行测试之前,让我们确认我们的智能合约可以成功编译,使用以下命令:

dapp build

如果编译成功,你将看到以下内容:

+ dapp clean
+ rm -rf out

要运行测试,请执行:

dapp test

你将看到以下输出:

dapp test
Running 6 tests for src/EscrowContract.t.sol:EscrowTest
[PASS] testEmergencyWithdraw() (gas: 138756)
[PASS] testFailDistributeInvalidId() (gas: 4451)
[PASS] testFailDepositNotWhitelisted() (gas: 10122)
[PASS] testDistribute() (gas: 241956)
[PASS] testDeposit() (gas: 130555)
[PASS] testAddParticipants() (gas: 249315)

接下来,让我们将 Escrow 合约部署到 Sepolia 测试网。

部署到 Sepolia 测试网

要将我们的 Escrow 合约部署到 Sepolia,我们将使用 dapp create。首先,让我们设置我们的环境。在项目根目录中创建一个 ~/.sethrc 文件并填写以下值:

export ETH_RPC_URL=YOUR_RPC_URL
export ETH_FROM=YOUR_WALLET_ADDRESS
export ETH_GAS=2000000
export ETH_PRIO_FEE=$(seth --to-wei 0.1 gwei)

请记住在 ETH_RPC_URLETH_FROM 中填写正确的值。对于其他 gas 值,根据当前市场情况进行调整。

接下来,让我们设置要使用的钱包以部署 Escrow 合约。在项目根目录的终端中运行以下命令:

ethsign import

系统将提示你输入私钥和一个密码。

现在,让我们构建并部署合约:

dapp build
dapp create src/EscrowContract.sol:Escrow

Dapp Tools 将提示你输入密钥库密码,然后部署合约。它将输出交易哈希和已部署合约地址。

Ethereum account passphrase (not echoed): seth-send: Published transaction with 6528 bytes of calldata.
seth-send: 0x7a5918711c0c0bbc18da3ac57e664ac48a0b1102fa0301f01ac71d31ef1b5df6
seth-send: Waiting for transaction receipt..........................
seth-send: Transaction included in block 6808530.
0xc9e5ba29E85f5F39333A3B2DF238fff40fcA36D0

接下来,让我们介绍如何与已部署的 Escrow 合约进行交互。

与已部署合约进行交互

我们可以使用 seth(Dapp Tools 的一部分)与我们已部署的合约进行交互。首先,让我们为已部署合约地址设置一个全局值。在你的终端窗口中运行以下命令,并输入你的 Escrow 合约地址:

CONTRACT_ADDRESS=YOUR_DEPLOYED_ESCROW_CONTRACT_ADDRESS

以下是一些示例命令:

要将参与者添加到白名单(作为管理员):

seth send $CONTRACT_ADDRESS "addParticipants(address[])" "[0xb84c..., 0x894c...]"

作为白名单参与者存入 0.01 ETH:

seth send $CONTRACT_ADDRESS "depositFunds()" --value $(seth --to-wei 0.01 eth)

注意,你需要在 .sethrc 中重新配置 FROM_ADDRESS 为参与者地址。

作为管理员将 0.01 ETH 分配给参与者:

seth send $CONTRACT_ADDRESS "distribute(uint256,uint256)" 1 $(seth --to-wei 0.01 eth)

注意,分配函数需要一个参与者 ID(从 1 开始)。

获取参与者的余额:

seth call $CONTRACT_ADDRESS "getBalance(address)(uint256)" "PARTICIPANT_ADDRESS"

记得在调用具有 onlyAdminonlyWhitelisted 修饰符的函数时使用正确的地址(管理员或参与者)。

参考资料

最后想法

Dapp Tools 为以太坊开发提供了一套强大的实用工具。通过利用其测试、部署和交互能力,你可以简化智能合约开发过程,创建更强大、经过良好测试的合约。

我们希望看到你正在创建的内容!在 Discord 或推特上与我们分享你的项目。如果你对本指南有任何反馈或问题,我们很乐意听到你的声音!

我们 ❤️ 反馈!

让我们知道 如果你有任何反馈或新主题请求。我们期待你的反馈。

  • 原文链接: quicknode.com/guides/eth...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。