如何构建一个DApp:完整的DApp架构

1

概览

去中心化应用(DApp)重新定义了应用程序在Web3中的构建、管理和交互方式。通过利用区块链技术,DApp提供了一个安全、透明且无需信任的系统,在无需任何中央授权的情况下即可实现点对点交互。DApp架构的核心由几个主要组件组成,它们协同工作以创建一个强大且去中心化的生态系统。这些组件包括智能合约、节点、前端用户界面等。

在本教程中,您将通过编写一个用以铸造Token的完整DApp来面对面了解每个主要组件。我们还将探索DApp的其他可选组件,这些组件可以增强您未来项目的用户体验。您可以在GitHub上的monorepo中查看完整的项目内容。

查看先决条件

要开始进行操作,您需要具备以下条件:

  • 一个拥有DEV的Moonbase Alpha账户 您可以每24小时一次从Moonbase Alpha水龙头上获取DEV代币以在Moonbase Alpha上进行测试
  • 已安装版本16或是以上的Node.js
  • VS Code与Juan Blanco的Solidity扩展是推荐的IDE
  • JavaScript和React的相关背景知识
  • 对于Solidity的基础了解。如果您并不熟悉Solidity,网络上有很多相关资源,包含Solidity范例教程。大约15分钟的快速学习即可用于本教程之中
  • 已安装类似于MetaMask的钱包

节点和JSON-RPC端点

一般来说,JSON-RPC是一种利用JSON对数据进行编码的远程过程调用(RPC)协议。在Web3产业中,它们指的是DApp开发者用来发送请求和接收来自区块链节点响应的特定JSON-RPC,这让它成为与智能合约交互的关键元素。它们允许前端用户界面与智能合约无缝交互,并为用户提供有关其操作的实时反馈。它们还允许开发者先部署他们的智能合约!

要让JSON-RPC与Moonbeam区块链进行通信,您需要运行一个节点。但这可能是昂贵、复杂且麻烦的。幸运的是,只要您有权访问节点,就可以与区块链进行交互。Moonbase Alpha有一些免费和付费节点选项。在本教程中,我们将使用Moonbeam基金会的Moonbase Alpha公共节点,但建议您获取自己的私有端点

https://rpc.api.moonbase.moonbeam.network

现在您有了一个URL。您会如何使用它?通过HTTPS,JSON-RPC请求是POST请求,其中包括用于读取和写入数据的特定函数,例如用于以只读方式执行智能合约功能的eth_call或用于将签名交易提交到的网络eth_sendRawTransaction(为改变区块链状态的调用)。整个JSON请求结构将始终具有类似于以下的结构:

{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_getBalance",
    "params": ["0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac", "latest"]
}

此范例为使用0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac账户的余额(在Moonbase Alpha中为DEV)。让我们解析其中的元素:

  • jsonrpc — JSON-RPC APR版本,通常为“2.0”
  • id — 用于定义请求回应的常数值,通常可以保持为
  • method — 特定从/至区块链读/写数据的函数。您可以在我们的文档网站查看许多不同的RPC函数
  • params — 根据特定method的输入参数阵列

事实上还有其他额外的元素会被加入至JSON-RPC请求中,但这四个最为常见。

在当前,这些JSON-RPC请求非常有用,但是在编写代码时,一遍又一遍地创建JSON对象可能会很麻烦。这就是为什么存在有助于抽象和促进这些请求的使用的库。Moonbeam提供了许多库的文档,我们将在本教程中使用Ethers.js。您仅需要了解,每当我们通过Ethers.js包与区块链交互时,我们实际上是在使用 JSON-RPC!

智能合约

智能合约是自动执行的合约,协议条款将会直接被写入代码中。它们充当任何DApp的去中心化后端,自动化并强制执行系统内的商业逻辑。

如果您来自传统的Web开发背景,智能合约的用意旨在取代后端,但需要注意的是:用户必须拥有原生Token(GLMR、MOVR、DEV等)才能发出状态更改请求,存储信息可能会很昂贵,并且存储的信息均不为仅自己可见

当您将智能合约部署到Moonbeam上时,您会上传一系列EVM或以太坊虚拟机可以理解的指令。每当有人与智能合约交互时,EVM都会执行这些透明、防篡改且不可变的指令来更改区块链的状态。在智能合约中正确编写指令非常重要,因为区块链的状态定义了有关DApp的最关键信息,例如谁拥有多少资金金额。

由于指令在低(组合)级别上很难编写和理解,因此我们使用Solidity等智能合约语言来简化它们的编写。为了帮助编写、调试、测试和编译这些智能合约语言,以太坊社区的开发者创建了开发者环境,例如HardhatFoundry。Moonbeam的开发者网站提供了有关大量开发者环境的信息。

本教程将会使用Hardhat管理智能合约。

创建一个Hardhat项目

您可以使用以下指令在Hardhat上发起项目:

npx hardhat init

创建JavaScript或TypeScript Hardhat项目时,系统会询问您是否要安装示例项目的依赖项,即为安装Hardhat和Hardhat Toolbox插件。您不需要工具箱中包含的所有插件,因此您可以安装Hardhat、Ethers和Hardhat Ethers插件,这就是本教程所需的全部内容:

npm install --save-dev hardhat @nomicfoundation/hardhat-ethers ethers@6

在我们开始编写智能合约之前,让我们先将JSON-RPC URL添加至配置之中,我们可以使用以下代码设置hardhat.config.js文件,并将INSERT_YOUR_PRIVATE_KEY取代为您具有资金账户的私钥。

注意事项

这仅用于测试目的,请勿将您具有真实资金的账户私钥以文字的方式储存

require('@nomicfoundation/hardhat-ethers');
module.exports = {
  solidity: '0.8.20',
  networks: {
    moonbase: {
      url: 'https://rpc.api.moonbase.moonbeam.network',
      chainId: 1287,
      accounts: ['INSERT_YOUR_PRIVATE_KEY']
    }
  }
};

编写智能合约

本教程的目的是在创建一个允许您以一个价格铸造Token的DApp,让我们在这部分编写关于此功能的智能合约!

当您已经发起一个Hardhat项目,您将能够在其contracts文件夹编写智能合约。其文件夹中拥有一个初始的智能合约,被称为Lock.sol,但您需要删除它并添加一个被称为MintableERC20.sol的智能文件。

Token的标准称为ERC-20,其中ERC代表“以太坊请求评论”。很久以前,这个标准就被定义了,现在大多数与Token配合使用的智能合约都期望Token具有ERC-20定义的所有功能。幸运的是,您不必凭记忆知道它,因为OpenZeppelin智能合约团队为我们提供了可供使用的智能合约基础。

安装OpenZeppelin智能合约

npm install @openzeppelin/contracts

现在,在您的MintableERC20.sol中添加以下代码:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MintableERC20 is ERC20, Ownable {
    constructor(address initialOwner) ERC20("Mintable ERC 20", "MERC") Ownable(initialOwner) {}
}

在编写智能合约时,您最终需要将他们进行编译。任何针对智能合约的开发环境皆有这个功能,在Hardhat中,您可以使用以下代码编译:

npx hardhat compile

在此应当能够顺利编译,将会由两个新的文件夹出现,分别为artifactscache。这两个文件夹存储了编译智能合约的信息。

让我们继续添加功能。您可以将以下常数、错误、事件和功能添加至您的Solidity文件:

    uint256 public constant MAX_TO_MINT = 1000 ether;

    event PurchaseOccurred(address minter, uint256 amount);
    error MustMintOverZero();
    error MintRequestOverMax();
    error FailedToSendEtherToOwner();

    /**Purchases some of the token with native currency. */
    function purchaseMint() payable external {
        // Calculate amount to mint
        uint256 amountToMint = msg.value;

        // Check for no errors
        if(amountToMint == 0) revert MustMintOverZero();
        if(amountToMint + totalSupply() > MAX_TO_MINT) revert MintRequestOverMax();

        // Send to owner
        (bool success, ) = owner().call{value: msg.value}("");
        if(!success) revert FailedToSendEtherToOwner();

        // Mint to user
        _mint(msg.sender, amountToMint);
        emit PurchaseOccurred(msg.sender, amountToMint);
    }

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MintableERC20 is ERC20, Ownable {
    constructor(address initialOwner) ERC20("Mintable ERC 20", "MERC") Ownable(initialOwner) {}

    uint256 public constant MAX_TO_MINT = 1000 ether;

    event PurchaseOccurred(address minter, uint256 amount);
    error MustMintOverZero();
    error MintRequestOverMax();
    error FailedToSendEtherToOwner();

    /**Purchases some of the token with native gas currency. */
    function purchaseMint() external payable {
        // Calculate amount to mint
        uint256 amountToMint = msg.value;

        // Check for no errors
        if (amountToMint == 0) revert MustMintOverZero();
        if (amountToMint + totalSupply() > MAX_TO_MINT)
            revert MintRequestOverMax();

        // Send to owner
        (bool success, ) = owner().call{value: msg.value}("");
        if (!success) revert FailedToSendEtherToOwner();

        // Mint to user
        _mint(msg.sender, amountToMint);
        emit PurchaseOccurred(msg.sender, amountToMint);
    }
}

此函数将允许用户传送原生Moonbeam currency(如GLMR、MOVR或DEV)作为价值,因为其为可支付函数。让我们根据不同部分解析此函数。

  1. 这将会根据传送的价值返还该铸造多少Token
  2. 接着它会检查铸造数量是否为0或是总铸造数量是否超过MAX_TO_MINT,并在两种情况中回传错误描述
  3. 合约接着会传送函数调用包含其中的数据至合约的所有者(默认为部署合约的地址,也就是您)
  4. 最后,Token将会铸造给用户,一个事件将会在其后发起

为确保所有流程顺利,让我们再次使用Hardhat:

npx hardhat compile

您现在已经完成您DApp的智能合约!如果这是一个生产应用,我们将会为其编写测试,但这并不包含在本教程的范围中。让我们接着进行部署。

部署智能合约

从本质上讲,Harhat是一个Node项目,它使用Ethers.js库与区块链进行交互。您还可以将Ethers.js与Hardhat的工具结合使用来创建脚本执行部署合约等操作。

您的Hardhat项目应当在文件夹中包含scripts脚本,被称为deploy.js。让我们以一个更加简单的脚本取代他。

const hre = require('hardhat');

async function main() {
  const [deployer] = await hre.ethers.getSigners();

  const MintableERC20 = await hre.ethers.getContractFactory('MintableERC20');
  const token = await MintableERC20.deploy(deployer.address);
  await token.waitForDeployment();

  // Get and print the contract address
  const myContractDeployedAddress = await token.getAddress();
  console.log(`Deployed to ${myContractDeployedAddress}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

该脚本使用Hardhat的Ethers库实例来获取我们之前编写的MintableERC20.sol智能合约的合约工厂。然后部署它并打印生成的智能合约的地址。使用Hardhat和Ethers.js库非常简单,但仅使用JSON-RPC就困难得多!

让我们在Moonbase Alpha上运行合约(其JSON-RPC端点先前在hardhat.config.js脚本中定义):

npx hardhat run scripts/deploy.js --network moonbase

您应当能见到展示Token地址的输出,确保您将其保存下来用于之后的教程中

Hardhat的内置智能合约部署解决方案并不优秀,它不会自动保存与部署相关的交易和地址!这就是创建hardhat-deploy包的原因。您能自己实现吗?或者您可以切换到不同的开发环境,例如Foundry

创建一个DApp前端

前端为用户提供与基于区块链应用交互的界面。 React是一种用于构建用户界面的流行JavaScript库,由于其基于组件的架构,可促进可重用代码和高效渲染,因此通常用于开发DApp前端。 useDApp包是一个为DApp设计基于Ethers.js的React框架,通过提供一套全面的钩子和组件来简化构建DApp前端的过程以及以太坊区块链功能的集成。

注意事项

一般来说,一个大型项目需要为他们的前端和智能合约在GitHub创建单独的库,但有些小型项目能够创建一个monorepo。

通过useDapp创建一个React项目

让我们设置一个新的React项目并安装依赖项,这可以在没有问题的情况下在我们的Hardhat项目中创建。create-react-app包将会为我们创建新的frontend目录:

npx create-react-app frontend
cd frontend
npm install ethers@5.6.9 @usedapp/core @mui/material @mui/system @emotion/react @emotion/styled

如果您还记得的话,Ethers.js是一个协助JSON-RPC通信的库。useDApp包是一个类似的库,它使用Ethers.js并将其格式化为React hooks,以便它们在前端项目中更好地工作。我们还添加了两个用于样式和组件的MUI包。

接下来,让我们设置位于frontend/src目录中的App.js文件以添加一些视觉结构:

import { useEthers } from '@usedapp/core';
import { Button, Grid, Card } from '@mui/material';
import { Box } from '@mui/system';

const styles = {
  box: { minHeight: '100vh', backgroundColor: '#1b3864' },
  vh100: { minHeight: '100vh' },
  card: { borderRadius: 4, padding: 4, maxWidth: '550px', width: '100%' },
  alignCenter: { textAlign: 'center' },
};

function App() {
  return (
    <Box sx={styles.box}>
      <Grid
        container
        direction='column'
        alignItems='center'
        justifyContent='center'
        style={styles.vh100}
      >
        {/* This is where we'll be putting our functional components! */}
      </Grid>
    </Box>
  );
}

export default App;

您可以通过在frontend库中运行以下指令开始React项目:

npm run start

您的前端将在localhost:3000上可用。

至此,我们的前端项目已经设置得足够好,可以开始处理功能代码了!

提供者、签名者和钱包

前端使用JSON-RPC与区块链通信,但我们将使用Ethers.js。当使用JSON-RPC时,Ethers.js喜欢将与区块链的交互程度抽象为对象,例如提供者、签名者和钱包。

提供者是前端用户界面和区块链网络之间的桥梁,促进通信和数据交换。它们抽象了与区块链交互的复杂性,提供了一个简单的API供前端使用。它们负责将DApp连接到特定的区块链节点,允许其从区块链读取数据,并且本质上包含JSON-RPC URL。

签名者是一种提供者,包含可用于签署交易的秘密。这允许前端创建交易,对其进行签名,然后使用eth_sendRawTransaction发送它们。签名者有多种类型,但我们最感兴趣的是钱包对象,它安全地存储和管理用户的私钥和数字资产。MetaMask等钱包通过通用且用户友好的流程促进交易签名。它们充当DApp中用户的代表,确保仅执行授权的交易。Ethers.js钱包对象代表我们前端代码中的此接口。

通常,使用Ethers.js的前端将要求您创建一个提供者,连接到用户的钱包(如适用),并创建一个钱包对象。在较大的项目中,这个过程可能会变得难以处理,尤其是在MetaMask之外还存在大量钱包的情况下。

幸运的是,我们安装了useDApp包,这为我们简化了许多流程。这同时也抽象了以太坊正在做的事情,这就是为什么我们在这里花了一些时间来解释它们。

创建一个提供者

让我们对useDApp包进行一些设置。首先,在React前端的index.js文件(位于frontend/src目录中)中,添加一个DAppProvider对象及其配置。这本质上充当Ethers.js提供程序对象,但可以通过useDApp hook在整个项目中使用:

需要开发的小伙伴可以关注博士!!
区块链开发、项目包装孵化一站式服务!!
  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
开发一个dapp需要设计一个合理的经济模型,下面是一些设计思路供参考: 1. 目标和用途:首先需要确定dapp的目标和用途,以此为基础设计经济模型。比如,如果是一个游戏类dapp,可以考虑设计一种激励机制来鼓励用户玩游戏。 2. 代币设计:选择代币类型,比如ERC20代币,确定代币总量、分配方式、销毁机制等。代币经济模型应该能够平衡代币的供求关系,保持代币的稳定性。 3. 激励机制:设计一种激励机制来鼓励用户参与dapp。比如,可以设计一种奖励机制,当用户完成一些任务或达到一定的贡献值时,可以获得一定数量的代币奖励。 4. 治理机制:设计一种治理机制来确保dapp的稳定和发展。比如,可以设计一种投票机制,让代币持有者参与dapp的运营和决策。 5. 安全性和可持续性:经济模型应该考虑到dapp的安全性和可持续性问题。比如,设计一种防止欺诈和攻击的机制,确保代币的安全;同时,经济模型应该能够支持dapp的长期发展。 6. 实验和改进:设计一个可调整的经济模型,通过实验和改进来优化模型。比如,在dapp运营的过程中,可以通过不断地调整激励机制和治理机制来优化经济模型。 综上所述,设计一个合理的经济模型需要考虑多个方面,包括代币设计、激励机制、治理机制、安全性和可持续性等。经济模型应该是可调整和可持续的,以适应dapp的发展需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JD161222

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值