共计 8847 个字符,预计需要花费 23 分钟才能阅读完成。
去中心化利用,或者叫 dApp,是一种不依赖于中心化服务器的利用。相同,dApp 应用像是区块链和预言机这些 Web3 技术,来实现本人的逻辑和后盾性能,具备不可篡改和平安的个性。
在这个技术教程中,你会学习到怎么开发一个 end-to-end 的 dApp。在 dApp 中,用户能够通过一个智能合约,获取和存储 ETH 的以后价格。这个教程 demo 代码存储在 Github 中。
要求
你须要先装置以下软件:
- NodeJS
-
MetaMask
去中心化利用是什么?
与传统的 App 在中心化服务器运行后端代码不同的是,dApp 的后端代码是运行在区块链上的。当然,dApp 的前端代码和 UI 能够应用任何语言开发,能够部署在任何服务器上与后端逻辑相交互。
因为 dApp 能够通过安全性很高且不可篡改的智能合约来承载后端逻辑,所以 dApp 有很多 Web2 零碎中没有的劣势:
- 不会宕机
- 隐衷性更强
- 抗操纵
- 在最小信赖环境下执行逻辑
然而,这些劣势也带来了对应的毛病。因为代码是部署在区块链上,这些逻辑默认是无奈批改的,所以 dApp 的保护难度比拟高。除此以外,因为代码是运行在分布式网络中,而不是中心化服务器,所以性能会比拟低。另外,因为用户须要有 Web3 钱包并且通过有足够的加密资产来领取手续费,所以用户体验也会降落。
dApp 组件
dApp 的组件会有三个不同的类型:智能合约,前端逻辑(UI)和数据存储。
智能合约
智能合约存储了 dApp 的业务逻辑和以后的状态,这个是 dApp 和传统网络应用的最大区别,也正是因为这一点让 dApp 具备了以上提到过的劣势。
前端 / UI
只管后端逻辑须要开发者实现智能合约代码,并把它部署在区块链上,然而在前端,开发者还是应用规范的网络技术,比方 HTML 和 javascript,因而开发者能够应用本人相熟的工具,库和框架。客户端的 UI 通常通过 Web3.js 和 Ether.js 与智能合约交互。像是对信息进行签名并且发送给智能合约这些操作,通常是通过浏览器的 Web3 钱包 MetaMask 实现。
数据存储
大多数利用须要存储数据,然而因为区块链分布式的特点,在链上存储大量的数据效率很低,而且十分贵。这也是为什么许多 dApp 须要应用 IPFS 或者 Filecoin 这样的链下存储服务来存储数据,只让区块链存储重要的业务逻辑和状态。
当然你也能够抉择传统的云存储服务,然而还是有很多开发者抉择分布式存储,因为区块链利用能够提供最小信赖的个性。
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
接下来,我通过 VS Code 关上创立好的文件夹,而后装置 Hardhat:
npm init -y
npm install --save-dev hardhat
npx hardhat
(choose create javascript project, choose default parameters)
当装置实现之后,在“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;
创立一个新的变量和函数,在智能合约上贮存这个值。
int public storedPrice;
而后,创立一个新的函数,它会被 dApp 的前端调用。这个函数会通过调用 getLatestPrice 函数查看 Ethereum 的最新价格,而后将这个值存储在 storedPrice 这个参数中:
function storeLatestPrice() external {storedPrice = getLatestPrice();
}
你的新的合约应该和上面的一样:
// 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();
}
}
第二步:部署智能合约
当初你曾经能够在 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
而后,将 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",
};
下一步是在 backend 文件夹中创立一个 .env 文件。而后你须要从 Web3 钱包中获取你的私钥,而后粘贴到 PRIVATE_KEY 这一行。请再确定一下,为了平安你在这个例子中最好应用一个在主网上没有任何的资产的新 Web3 钱包。
当这些做完当前,你须要一个 RPC endpoint 来接入 Rinkeby 网络。你能够将它粘贴到 .env 文件的 RINKEBY_RPC_URL 中的 RPC URL 中。咱们举荐注册一个收费的 Infura 或者 Alchemy 账户以获取一个 RPC URL。
创立 .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 <script>`.
//
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
// will compile your contracts, add the Hardhat Runtime Environment's members to the
// global scope, and execute the script.
const hre = require("hardhat");
async function main() {const PriceConsumer = await hre.ethers.getContractFactory("PriceConsumerV3");
const priceConsumer = await PriceConsumer.deploy();
await priceConsumer.deployed();
console.log("Contract deployed to:", priceConsumer.address);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {console.error(error);
process.exitCode = 1;
});
当初你曾经能够通过 Hardhat 来编译你的智能合约并且把它部署在 Rinkeby 网络中:
npx hardhat compile
npx hardhat run --network rinkeby scripts/deploy.js
你当初应该看到相似上面这行的信息,会展现你部署在 Rinkeby 网络上的智能合约地址。留神这个地址,咱们在前面的步骤中须要用到它。
部署的智能合约
祝贺,你曾经实现了 dApp 的合约局部!
第三步:创立前端利用
dApp 的前端逻辑和 UI 能够通过各种框架实现。
React 是最受欢迎的 Javascript 代码库之一,它能够用来开发功能丰富的网页,因而也被许多 Web3 dApp 所应用。除此之外,Ether.js 是一个 Javascript 库,它是用来和 EVM 区块链连贯和交互的。当你把这两者联合起来,就能够开始开发你的 dApp 的前端的了。
在这部分,咱们将应用 create-react-app 创立一个新的 React 利用。而后我会介绍如何通过 Ether.js 来将 UI 和曾经部署的智能合约连接起来,实现一个 end-to-end 的 dApp。
创立 React 利用
开发前端代码之前,须要先装置和初始化一个 cerate-react-app 我的项目,而后批改它以满足咱们的 dApp。第一步将这个库装置到“frontend”文件夹:
cd ..
npx create-react-app frontend
这一步实现后,你应该能够在“frontend”文件夹中看到所有相干的 React 代码。关上“frontend”文件夹而后做以下操作:
- 删除 /src/setupTests.js
- 删除 /src/ReportWebVitals.js
- 删除 /src/logo.svg
- 删除 /src/App.test.js
- 删除 /src/App.css
文件夹构造应该如下所示:
React front-end 文件夹构造
在批改 React 利用代码之前,咱们须要先装置 Bootstrap 和 Ether.js。Bootstrap 是一个很风行的前端 CSS 框架,有很多 React 能够应用的 UI widgets 和 CSS 款式。Ether.js 能够将前端代码与区块链上曾经部署的智能合约相连接。在“frontend”文件夹中输出以下命令:
cd frontend
npm install bootstrap
npm install ethers
当初咱们能够开始批改 React 利用的代码,在 /src/ 文件夹中关上 App.js 文件,而后删掉这些内容。咱们从 0 开始编写。
第一步是通知 app 咱们想要应用 React(包含 useEffect 和 useState 库)和 Ether.js:
import React, {useEffect, useState} from 'react';
import {ethers} from "ethers";
下一步,创立一个叫“App”的函数而后 export 它:
function App() {}
export default App;
- 当初咱们将开始实现“App”函数的代码。退出上面的代码,这些代码会做以下的操作:
- 建设 storePrice 和 setStoresPrice 的 react hook。
-
连贯你的 Metamask Web3 钱包。
- 设置曾经部署的智能合约地址和 ABI。Ether.js 在与曾经部署的合约交互的时候须要这两个信息。把智能合约地址这个值(能够在部署的时候取得)插入到
REPLACE_WITH_DEPLOYED_CONTRACT_ADDRESS
这里。
- 智能合约的 ABI 能够从文件
/backend/artifacts/contracts/PriceConsumerV3.json
中取得,你还能够应用 code minifier 对它进行更好的格式化,存储在你的利用中。
- 设置曾经部署的智能合约地址和 ABI。Ether.js 在与曾经部署的合约交互的时候须要这两个信息。把智能合约地址这个值(能够在部署的时候取得)插入到
const [storedPrice, setStoredPrice] = useState('');
const provider = new ethers.providers.Web3Provider(window.ethereum)
const signer = provider.getSigner()
const contractAddress = <REPLACE_WITH_DEPLOYED_CONTRACT_ADDRESS>’';
const ABI =
'[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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 contract = new ethers.Contract(contractAddress, ABI, signer);
当初咱们在利用中创立两个函数:
- getStoredPrice 会连贯部署的合约,并且通过 storedPrice() 获取以后价格。
- setNewPrice 会调用已部署合约的 storeLatestPrice 函数,等到交易实现,而后调用 getStoredPrice 函数来获取存储在智能合约中的价格。
咱们会在利用中退出 getStoredPrice 函数,它会在加载页面的时候调用 getter 函数:
const getStoredPrice = async () => {
try {const contractPrice = await contract.storedPrice();
setStoredPrice(parseInt(contractPrice) / 100000000);
} catch (error) {console.log("getStoredPrice Error:", error);
}
}
async function updateNewPrice() {
try {const transaction = await contract.storeLatestPrice();
await transaction.wait();
await getStoredPrice();} catch (error) {console.log("updateNewPrice Error:", error);
}
}
getStoredPrice()
.catch(console.error)
前端代码的最初一步是返回 JSX 代码以让浏览器 render。将上面的代码复制进 App 的函数中,在 getStorePrice() 的上面。这些代码会做上面的操作:
- 返回一个简略的 2 列 grid layout。
- 第一列蕴含了智能合约中存储的 ETH/USD 价格。
-
第二列蕴含了一个按钮,用户能够应用这个按钮来与智能合约交互,更新存储的价格。点击按钮,而后调用上面的 setNewPrice 函数。
return ( <div className="container"> <div className="row mt-5"> <div className="col"> <h3>Stored Price</h3> <p>Stored ETH/USD Price: {storedPrice}</p> </div> <div className="col"> <h3>Update Price</h3> <button type="submit" className="btn btn-dark" onClick={updateNewPrice}>Update</button> </div> </div> </div> );
你的利用当初曾经实现了。如果须要,你能够和这里残缺代码比拟,保障你的代码中没有谬误。你能够运行你的 dApp 了。
运行你的 dApp
在确认你所有的文件都曾经存储当前,在 frontend 文件夹中运行以下命令来启动你的 dApp:
npm run start
在利用被载入当前,浏览器中会有一个新的窗口,展现 dApp 的 UI,你应该从 Metamask 看到一个弹出的告诉,申请将钱包连贯到这个利用上。
React 前端
在查看你在 Metamask 的账户中有一些 Rinkeby ETH 当前,点击“Update”按钮,就能够和你曾经部署好的智能合约进行交互。你应该会收到 Metamask 的告诉,申请你确认交易。在你实现这些当前,过几秒你的 dApp 会主动刷新,而后以后的 Ethereum 会呈现在“Stored Price”区域:
React 前端展现 Data Feed 后果
祝贺,你曾经胜利创立,部署并且交互了一个简略的 dApp!在这个教程中,你只是在你的电脑上运行了一个本地前端,同时你也能够把它部署在云服务器中,或者应用去中心化版本的前端,能够将其部署在 IPFS 中!你也能够批改利用的 CSS 来扭转 UI 让它更合乎你的应用场景。
总结
去中心化利用能够用像是区块链和智能合约这些 Web3 科技代替传统的后端服务器,带来传统利用没有安全性和抗操纵的特点。
在这个 demo 中,咱们创立了一个简略的 dApp,dApp 中蕴含了一个智能合约,这个智能合约能够从 Chainlink ETH/USD 喂价对中取得最新的价格,而后贮存在智能合约之中。而后咱们创立了一个简略的 UI,应用了 React 和 Ether.js 连贯并且与部署好的合约相交互。
您能够关注 Chainlink 预言机并且私信退出开发者社区,有大量对于智能合约的学习材料以及对于区块链的话题!