以太坊 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-示例 cd chainlink-dapp-示例 mkdir 后端 光盘后端
接下来,通过 VS Code 打开创建好的文件夹,然后安装 Hardhat
npm 初始化 -y npm install --save-dev 安全帽 NPX安全帽 (选择创建javascript项目,选择默认参数)
当安装完成之后,在“contracts”文件夹中删掉 Touch.sol ,然后在这个文件夹中创建一个名为 PriceConsumerV3.sol 的文件。在这个文件中将存储我们的合约,所以将 Chainlink 文档中的代码复制到这个文件中,然后保存。
在示例代码中,您会看到演示合约已经有一个名为 getLatestPrice 的功能来通过 Rinkeby 上的 ETH/USD 喂价来查看以太坊的当前价格。
函数 getLatestPrice() 公共视图返回 (int) { ( /*uint80 roundID*/, 国际价格, /*uint 开始时间*/, /*uint 时间戳*/, /*uint80 轮回回答*/ ) = PriceFeed.latestRoundData(); 退货价格;
创建一个新的变量和函数,在智能合约上存储这个值。
int 公共存储价格;
然后,创建一个新的函数,它会被 dApp 的前端调用。这个函数会通过 getLatestPrice 函数查看以太坊的最新价格,然后将该值存储在storedPrice这个参数中:
函数 storeLatestPrice() 外部 { 存储价格 = getLatestPrice(); }
你的新合约应该和下面的一样:
// SPDX 许可证标识符:MIT 杂注可靠性^0.8.7; 导入“@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol”; 合约 PriceConsumerV3 { AggregatorV3接口内部priceFeed; int 公共存储价格; /** * 网络:林克比 * 聚合器:ETH/USD * 地址:0x8A753747A1Fa494EC906cE90E9f37563A8AF630e */ 构造函数(){ 供给价格 = 聚合器V3接口(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e); } /** * 返回最新价格 */ 函数 getLatestPrice() 公共视图返回 (int) { ( /*uint80 roundID*/, 国际价格, /*uint 开始时间*/, /*uint 时间戳*/, /*uint80 轮回回答*/ ) = PriceFeed.latestRoundData(); 退货价格; } 函数 storeLatestPrice() 外部 { 存储价格 = getLatestPrice(); } }
现在您已经可以在 Rinkeby 测试网中编译并部署您的合约了,如果没有测试网的通证的话,可以在网页链接{Chainlink 水龙头}获得一些。
如果您使用的是 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
然后,将hardhat-config.js文件中的内容换成下面的内容:
要求(“@nomicfoundation/hardhat-toolbox”); //require(“@nomiclabs/hardhat-ethers”) require('dotenv').config() const RINKEBY_RPC_URL = process.env.RINKEBY_RPC_URL || “网页链接” const PRIVATE_KEY = process.env.PRIVATE_KEY || “abcdef” 模块. 导出 = { 默认网络:“rinkeby”, 网络:{ 安全帽: { // // 如果你想进行一些分叉,请取消注释 // 分叉: { // url: MAINNET_RPC_URL // } }, 本地主机:{ }, 林克比:{ 网址:RINKEBY_RPC_URL, 帐户:[PRIVATE_KEY], 保存部署:true, }, }, 坚固性:“0.8.9”, };
下一步是在后端文件夹中创建一个 .env 文件。然后你需要从网页链接{Web3 钱包中获取你的私钥},然后粘贴到 PRIVATE_KEY 这行。请再确定一下,为了安全你在这个例子中最好使用一个在主网上没有任何资产的新Web3钱包。
当这些完成以后,你需要一个 RPC 端点来接入 Rinkeby 网络。你可以将它粘贴到 .env 文件的 RINKEBY_RPC_URL 中的 RPC URL 中。我们推荐注册一个免费的 Infura 或者 Alchemy 账户来获取一个RPC URL。
创建.env文件
下一步是修改“script”文件夹中deploy.js文件中的内容,使得它可以配置你的新合约。打开文件,然后将代码替换为下列代码。
// 我们在这里明确要求 Hardhat 运行时环境。这是可选的 // 但对于通过 `node <script>` 以独立方式运行脚本很有用。 // // 您还可以使用“npx Hardhat run <script>”运行脚本。如果你这样做,安全帽 // 将编译您的合约,将 Hardhat 运行时环境的成员添加到 // 全局范围,并执行脚本。 const hre = require("安全帽"); 异步函数 main() { const PriceConsumer =等待 hre.ethers.getContractFactory("PriceConsumerV3"); const PriceConsumer = 等待 PriceConsumer.deploy(); 等待 PriceConsumer.deployed(); console.log("合约部署到:",priceConsumer.address); } // 我们推荐这种模式,以便能够在任何地方使用 async/await // 并正确处理错误。 main().catch((错误) => { 控制台.错误(错误); 进程.exitCode = 1; });
现在您已经可以通过 Hardhat 来编译您的智能合约并将其部署在 Rinkeby 网络中:
npxhardhat编译 npxhardhat运行--network rinkeby脚本/deploy.js
您现在应该看到类似下面这行的信息,会显示您在 Rinkeby 网络上部署的智能合约地址。注意这个地址,我们在后面的步骤中需要使用它。
部署的智能合约
恭喜,您已经完成了 dApp 的合约部分!
dApp的接入逻辑和UI可以通过各种框架完成。
React是最受欢迎的 Javascript 代码库之一,它可以用于开发功能丰富的网页,因此也被许多 Web3 dApp 所使用。除此之外,Ether.js 是一个 Javascript 库,它用于和EVM区块链连接和交互的。当你把这二者结合在一起时,就可以开始开发你的 dApp 的接口了。
在这部分,我们将使用create-react-app创建一个新的 React 应用。然后我将介绍如何通过 Ether.js 来将 UI 和已经部署的智能合约连接起来,完成一个端到端的 dApp。
开发前端代码之前,需要先安装并初始化一个 cerate-react-app 项目,然后修改它相当于我们的 dApp。第一步将这个库安装到“frontend”文件夹:
cd .. npx create-react-app 前端
这一步完成后,你应该可以在“frontend”文件夹中看到所有相关的React代码。打开“frontend”文件夹然后执行以下操作:
删除/src/setupTests.js
删除 /src/ReportWebVitals.js
删除 /src/logo.svg
删除/src/App.test.js
删除/src/App.css
文件夹结构应该如下所示:
React前端文件夹结构
在修改 React 应用代码之前,我们需要先安装Bootstrap和Ether.js。Bootstrap 是一个很流行的前端 CSS 框架,有很多 React 可以使用的 UI 小部件和 CSS 样式。Ether.js 可以将前端代码与区块链上已经部署的智能合约相连接。在“frontend”文件夹中输入以下命令:
cd 前端 npm 安装 bootstrap npm 安装 ethers
现在我们可以开始修改React应用的代码,在/src/文件夹中打开App.js文件,然后删掉这些内容。我们从0开始编写。
第一步是告诉应用程序我们想要使用 React(包括 useEffect 和 useState 库)和 Ether.js:
从 'react' 导入 React, { useEffect, useState }; 从“以太”导入{以太};
下一步,创建一个名为“App”的函数然后导出它:
函数应用程序(){ } 导出默认应用程序;
现在我们将开始完成“App”函数的代码。加入下面的代码,这些代码会做以下的操作:
创建 storePrice 和 setStoresPrice 的react hook。
连接你的 Metamask Web3 钱包。
设置已经部署的智能合约地址和 ABI。Ether.js 在与已经部署的智能合约交互的时候需要这两个信息。把智能合约地址这个值(可以在部署的时候获得)插入到REPLACE_WITH_DEPLOYED_CONTRACT_ADDRESS这里。智能合约的 ABI 可以从文件 /backend/artifacts/contracts/PriceConsumerV3.json 中获得,您还可以使用代码压缩器对它进行更好的格式化,在您的应用中。
const [storedPrice, setStoredPrice] = useState(''); const 提供者 = new ethers.providers.Web3Provider(window.ethereum) const 签名者=provider.getSigner() const ContractAddress = <REPLACE_WITH_DEPLOYED_CONTRACT_ADDRESS>''; 常量 ABI = '[{"inputs":[],"stateMutability":"nonpayable","type":"构造函数"},{"inputs":[],"name":"getLatestPrice","outputs":[{" InternalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[]," name":"storeLatestPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storedPrice","outputs" :[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"}]' const 合约 = new ethers.Contract(contractAddress, ABI, 签名者);
现在我们在应用程序中创建两个函数:
getStoredPrice 会连接配置的合约,并且通过storedPrice() 获取当前价格。
setNewPrice 会调用已部署合约的 storeLatestPrice 函数,直到交易完成,然后调用 getStoredPrice 函数来获取存储在智能合约中的价格。
我们会在应用中加入 getStoredPrice 函数,它会在加载页面的时候调用 getter 函数:
const getStoredPrice = async () => { 尝试 { const ContractPrice =等待contract.storedPrice(); setStoredPrice(parseInt(contractPrice) / 100000000); } 捕获(错误){ console.log("getStoredPrice 错误:", error); } } 异步函数 updateNewPrice() { 尝试 { const transaction =等待contract.storeLatestPrice(); 等待事务.wait(); 等待 getStoredPrice(); } 捕获(错误){ console.log("更新新价格错误:", error); } } 获取存储价格() .catch(控制台.错误)
继承代码的最后一步是返回 JSX 代码以让浏览器渲染。将下面的代码复制进 App 的函数中,在 getStorePrice() 的下面。这些代码会做下面的操作:
返回一个简单的2列网格布局。
第一列包含智能合约中存储的 ETH/USD 价格。
第二列包含了一个按钮,用户可以使用这个按钮来与智能合约,更新交易存储的价格。点击按钮,然后调用下面的 setNewPrice 函数。
返回 ( <div className="容器"> <div className="row mt-5"> <div className="col"> <h3>存储价格</h3> <p>存储的 ETH/美元价格:{storedPrice}</p> </div> <div className="col"> <h3>更新价格</h3> <按钮类型=“提交”className=“btn btn-dark” onClick={updateNewPrice}>更新</button> </div> </div> </div> );
你的应用程序现在已经完成了。如果需要,你可以和这里的完整代码进行比较,保证你的代码中没有错误。你可以运行你的 dApp 了。
在确认你所有的文件都已经存储以后,在前端文件夹中运行以下命令来启动你的 dApp:
npm 运行开始
在应用被加载以后,浏览器中会有一个新的窗口,显示 dApp 的 UI,你应该从 Metamask 看到一个弹出的通知,请求将钱包连接到这个应用上。
反应前端
在检查你在 Metamask 的账户中有一些 Rinkeby ETH 以后,点击“更新”按钮,就可以和你已经配置好的智能合约进行交易了。你应该会收到 Metamask 的通知,请求你确认交易。在你完成后这些以后,过几秒你的 dApp 会自动刷新,然后当前的以太坊会出现在“Stored Price”区域:
React前端展示Data Feed结果
恭喜,您已经成功创建、部署并且交互了一个简单的 dApp!在本教程中,您只需在您的电脑上运行了一个本地接口,同时您也可以将其部署在云服务器中,或者使用去中心化版本的前端,可以将其部署在IPFS中!你也可以修改应用的CSS来改变UI以使更符合你的使用场景。
去中心化应用可以用区块链和智能合约这些Web3科技替代传统的头部服务器,带来传统应用没有安全性和抗集群的特点。
在这个演示中,我们创建了一个简单的 dApp,dApp 中包含了一个智能合约,这个智能合约可以从 Chainlink ETH/USD 喂价中获得最新的价格,然后存储在智能合约中。然后我们创建了一个简单的 UI,使用了 React 和 Ether.js 连接并且与部署良好的交互。