• 开发 dApp 的三个步骤


    去中心化应用,或者叫 dApp,是一种不依赖于中心化服务器的应用。相反,dApp 使用像是区块链和预言机这些 Web3 技术,来实现自己的逻辑和后台功能,具备不可篡改和安全的特性。

    在这个技术教程中,你会学习到怎样开发一个 end-to-end 的 dApp。在 dApp 中,用户可以通过一个智能合约,获取和存储 ETH 的当前价格。这个教程 demo 代码存储在 Github 中。

    要求

    你需要先安装以下软件:

    去中心化应用是什么?

    与传统的 App 在中心化服务器运行后端代码不同的是,dApp 的后端代码是运行在区块链上的。当然,dApp 的前端代码和 UI 可以使用任何语言开发,可以部署在任何服务器上与后端逻辑相交互。

    因为 dApp 可以通过安全性很高且不可篡改的智能合约来承载后端逻辑,所以 dApp 有很多 Web2 系统中没有的优势:

    • 不会宕机
    • 隐私性更强
    • 抗操纵
    • 在最小信任环境下执行逻辑

    然而,这些优势也带来了对应的缺点。因为代码是部署在区块链上,这些逻辑默认是无法修改的,所以 dApp 的维护难度比较高。除此以外,因为代码是运行在分布式网络中,而不是中心化服务器,所以性能会比较低。另外,由于用户需要有 Web3 钱包并且通过有足够的加密资产来支付手续费,所以用户体验也会下降。

    dApp 组件

    dApp 的组件会有三个不同的类型:智能合约,前端逻辑(UI)和数据存储。

    智能合约

    智能合约存储了 dApp 的业务逻辑和当前的状态,这个是 dApp 和传统网络应用的最大区别,也正是因为这一点让 dApp 具备了以上提到过的优势。

    前端 / UI

    尽管后端逻辑需要开发者完成智能合约代码,并把它部署在区块链上,但是在前端,开发者还是使用标准的网络技术,比如 HTML 和 javascript,因此开发者可以使用自己熟悉的工具,库和框架。客户端的 UI 通常通过 Web3.jsEther.js 与智能合约交互。像是对信息进行签名并且发送给智能合约这些操作,通常是通过浏览器的 Web3 钱包 MetaMask 完成。

    数据存储

    大多数应用需要存储数据,但是因为区块链分布式的特点,在链上存储大量的数据效率很低,而且非常贵。这也是为什么许多 dApp 需要使用 IPFS 或者 Filecoin 这样的链下存储服务来存储数据,只让区块链存储重要的业务逻辑和状态。
    当然你也可以选择传统的云存储服务,然而还是有很多开发者选择分布式存储,因为区块链应用可以提供最小信任的特性。

    1-1.png
    Source以太坊 dApp 架构 来源:The Architecture of a Web3 Application

    现在我们知道了 dApp 的组件,让我们开发一个简单的 dApp。

    第一步:创建智能合约

    我们 dApp 中的智能合约是一个简单的例子,它可以查看数据并且反应出区块链上的变化。在这个例子中,我们会通过 Chainlink ETH/USD 喂价对查看 ETH/USD 的价格,然后将结果永久存储在智能合约上。

    第一步是打开 Chainlink 的文档,然后导航到 Using Data Feeds 页面。从这里将源代码复制进你的 IDE 中的一个新的文件里(比如 Visual Code),或者你可以点击“Open In Remix”按钮,然后使用在线 IDE Remix。

    在这个例子中,我们会使用 Visual Studio Code 和 Hardhat(一个 EVM 开发框架)。

    首先,为我们的 dApp 创建一个新的文件夹,并在这个文件夹中创建一个后端文件夹,用来存储智能合约代码:

    mkdir chainlink-dapp-example
    cd chainlink-dapp-example
    mkdir backend
    cd backend
    
    • 1
    • 2
    • 3
    • 4

    接下来,我通过 VS Code 打开创建好的文件夹,然后安装 Hardhat:

    npm init -y
    npm install --save-dev hardhat
    npx hardhat
    
    (choose create javascript project, choose default parameters)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当安装完成之后,在“contracts”文件夹中删掉 Touch.sol ,然后在这个文件夹中创建一个叫做 PriceConsumerV3.sol 的文件。在这个文件将存储我们的合约,所以将 Chainlink 文档中的代码复制到这个文件中,然后保存。

    在样例代码中,你会看到 demo 合约已经有一个叫做 getLatestPrice 的功能来通过 Rinkeby 上的 ETH/USD 喂价对查看 Ethereum 的当前价格。

    function getLatestPrice() public view returns (int) {
         (
            /*uint80 roundID*/,
            int price,
            /*uint startedAt*/,
            /*uint timeStamp*/,
            /*uint80 answeredInRound*/
         ) = priceFeed.latestRoundData();
         return price;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建一个新的变量和函数,在智能合约上储存这个值。

    int public storedPrice;
    
    • 1

    然后,创建一个新的函数,它会被 dApp 的前端调用。这个函数会通过调用 getLatestPrice 函数查看 Ethereum 的最新价格,然后将这个值存储在 storedPrice 这个参数中:

       function storeLatestPrice() external {
       
            storedPrice = getLatestPrice();
        }
    
    • 1
    • 2
    • 3
    • 4

    你的新的合约应该和下面的一样:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.7;
    
    import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
    
    contract PriceConsumerV3 {
    
        AggregatorV3Interface internal priceFeed;
        int public storedPrice;
    
        /**
         * Network: Rinkeby
         * Aggregator: ETH/USD
         * Address: 0x8A753747A1Fa494EC906cE90E9f37563A8AF630e
         */
        constructor() {
            priceFeed = 
    AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
        }
    
        /**
         * Returns the latest price
         */
        function getLatestPrice() public view returns (int) {
            (
                /*uint80 roundID*/,
                int price,
                /*uint startedAt*/,
                /*uint timeStamp*/,
                /*uint80 answeredInRound*/
            ) = priceFeed.latestRoundData();
            return price;
        }
    
        function storeLatestPrice() external {
            storedPrice = getLatestPrice();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    第二步:部署智能合约

    现在你已经可以在 Rinkeby 测试网中编译和部署你的合约了,如果没有测试网的通证的话,可以在 Chainlink faucet 获得一些。

    如果你使用的是 Remix 的话,你可以通过 Remix 编译和部署你的合约。如果你使用的是像是 Visual Studio Code 这样的 IDE 的话,我们推荐使用 Hardhat 来管理你的合约。

    在部署合约之前,第一步是安装 Hardhat 工具包,Chainlink 合约库和 dotenv 库。dotenv 可以将存储密码和敏感信息存储在一个单独的 .env 文件中:

    npm install --save-dev @nomicfoundation/hardhat-toolbox
    npm install @chainlink/contracts --save
    npm install dotenv
    
    • 1
    • 2
    • 3

    然后,将 hardhat-config.js 文件中的内容换成下面的内容:

    require("@nomicfoundation/hardhat-toolbox");
    
    //require("@nomiclabs/hardhat-ethers")
     require('dotenv').config()
    
     const RINKEBY_RPC_URL = process.env.RINKEBY_RPC_URL || 
    "https://eth-rinkeby.alchemyapi.io/v2/your-api-key"
     const PRIVATE_KEY = process.env.PRIVATE_KEY || "abcdef"
    
     module.exports = {
         defaultNetwork: "rinkeby",
         networks: {
             hardhat: {
                 // // If you want to do some forking, uncomment this
                 // forking: {
                 // url: MAINNET_RPC_URL
                 // }
             },
             localhost: {
             },
             rinkeby: {
                 url: RINKEBY_RPC_URL,
                 accounts: [PRIVATE_KEY],
                 saveDeployments: true,
             },
         },
    
      solidity: "0.8.9",
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    下一步是在 backend 文件夹中创建一个 .env 文件。然后你需要从 Web3 钱包中获取你的私钥,然后粘贴到 PRIVATE_KEY 这一行。请再确定一下,为了安全你在这个例子中最好使用一个在主网上没有任何的资产的新 Web3 钱包。

    当这些做完以后,你需要一个 RPC endpoint 来接入 Rinkeby 网络。你可以将它粘贴到 .env 文件的 RINKEBY_RPC_URL 中的 RPC URL 中。我们推荐注册一个免费的 Infura 或者 Alchemy 账户以获取一个 RPC URL。

    unnamed-1-1.png
    创建 .env 文件

    下一步是修改“script” 文件夹中 deploy.js 文件中的内容,使得它可以部署你的新合约。打开文件,然后将代码替换为下列代码。

    // We require the Hardhat Runtime Environment explicitly here. This is optional
    // but useful for running the script in a standalone fashion through `node