在 Web3 世界里,去中心化应用(dApp) 不只是区块链的代名词,更是人人都能上手的创新工具。本文将以“实时获取、存储 ETH / USD 价格”这一实际场景为例,手把手带你跑完 去中心化应用开发 全流程:编写智能合约 → 部署测试网 → 构建 React 前端。只需 3 步,让你从 0 进阶到可上线的 dApp!
悄悄告诉你:👉 点击这里,领取额外的实战技巧和隐藏合约模板
步骤 1|创建智能合约:用 Chainlink 喂价链上“记账”
1.1 技术选型 & 环境准备
- 开发框架:Hardhat(EVM友好、插件生态丰富)
- 语言:Solidity 0.8.x
- 喂价服务:Chainlink ETH/USD Data Feeds(免费、去中心化、可信)
- 工具钱包:MetaMask(前端交互必备)
1.2 初始化项目
mkdir chainlink-dapp-example && cd $_
mkdir backend && cd backend
npm init -y
npm install --save-dev hardhat
npx hardhat # 选「javascript」模板在 contracts 目录下创建文件 PriceConsumerV3.sol,复制并精简官方喂价合约示例。保留两个关键点:
getLatestPrice():只读,实时获取当前 ETH / USD 价格storeLatestPrice():写入,把拿到的价格存储到链上storedPrice
最终合约代码(删去冗余注释后):
// 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;
constructor() {
// Rinkeby 网络 ETH/USD 地址
priceFeed = AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
}
function getLatestPrice() public view returns (int) {
(,int price,,,) = priceFeed.latestRoundData();
return price;
}
function storeLatestPrice() external {
storedPrice = getLatestPrice();
}
}关键词:智能合约 | Chainlink | 喂价 | ETH/USD
步骤 2|部署合约至 Rinkeby 测试网
2.1 安装依赖
npm install --save-dev @nomicfoundation/hardhat-toolbox
npm install @chainlink/contracts dotenv2.2 配置 .env
在项目根目录建 .env,填入:
RINKEBY_RPC_URL=https://eth-rinkeby.alchemyapi.io/v2/<你的 key>
PRIVATE_KEY=<你的测试钱包私钥(0x开头)>钱包切记为空资产小号,保障测试安全!
2.3 Hardhat 参数
修改 hardhat.config.js:
require("@nomicfoundation/hardhat-toolbox");
require('dotenv').config();
module.exports = {
defaultNetwork: "rinkeby",
networks: {
rinkeby: {
url: process.env.RINKEBY_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
},
},
solidity: "0.8.9",
};2.4 部署脚本
编辑 scripts/deploy.js:
const hre = require("hardhat");
async function main() {
const PriceC = await hre.ethers.getContractFactory("PriceConsumerV3");
const pc = await PriceC.deploy();
await pc.deployed();
console.log("合约地址:", pc.address);
}
main();运行:
npx hardhat compile
npx hardhat run scripts/deploy.js --network rinkeby把打印出的 合约地址 复制保存,稍后前端要用!
步骤 3|构建 React 前端:连接钱包一键更新价
3.1 生成并裁切 React 项目
cd ..
npx create-react-app frontend
cd frontend
npm install ethers bootstrap删除默认的 logo.svg、App.css、App.test.js 等,轻装上阵。
3.2 App.js 核心实现
导入库
import React, { useEffect, useState } from 'react'; import { ethers } from 'ethers'; import 'bootstrap/dist/css/bootstrap.min.css';定义配置
const contractAddress = "<步骤2保存的合约地址>"; const ABI = [{"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"}]; function App() { const [price, setPrice] = useState(''); const [signer, setSigner] = useState(null); const [contract, setContract] = useState(null); useEffect(() => { if (window.ethereum) { const provider = new ethers.providers.Web3Provider(window.ethereum); const s = provider.getSigner(); setSigner(s); setContract(new ethers.Contract(contractAddress, ABI, s)); } }, []); const readStored = async () => { if (!contract) return; const val = await contract.storedPrice(); // Chainlink feed 精度 8 位 setPrice((val / 1e8).toFixed(2)); }; const writeStored = async () => { if (!contract) return; const tx = await contract.storeLatestPrice(); await tx.wait(); await readStored(); }; // 页面初次挂载就读一次 useEffect(() => { readStored(); }, [contract]); return ( <div className="container text-center mt-5"> <h1 className="mb-4">ETH / USD 价格展示 dApp</h1> <div className="row"> <div className="col-md-6"> <h3>链上存储价格</h3> <p className="fs-1 fw-bold">{price ? `¥ ${price}` : '—'}</p> </div> <div className="col-md-6"> <button className="btn btn-primary btn-lg" onClick={writeStored}> 同步最新价格 </button> </div> </div> </div> ); } export default App;
启动前端:
npm start浏览器自动打开 localhost:3000,连接 MetaMask 后即可一键更新价!
👉 想让 UI 变得更丝滑?速览这份前端优化锦囊
常见问题 FAQ
Q1:为什么要把价格再存一次,而不是前端直接读 getLatestPrice?
A:直接读也能拿到最新价,但会占用用户 gas;本例重点在于演示“写入”链上 storedPrice,供其他合约或前端持续引用,符合真实业务场景。
Q2:测试网 LINK 水龙头拿不到通证怎么办?
A:Chainlink Faucet 访问量大,可换 Hero Faucet、PARADIGM Faucet 或其他备用测试网源,24 小时后再试即可。
Q3:我可以在主网直接上线教程代码吗?
A:不建议!主网地址、私钥管理、合约审计、前端安全都要重新评估;测试网无风险打样最安全。
Q4:前端必须使用 React 吗?
A:不必须。Vue、Svelte、Next.js 甚至原生 HTML+JS 都可以,只要支持现代浏览器与 ethers.js 即可。
Q5:想把 ETH 换成 BTC 喂价怎么改?
A:Data Feeds 有 BTC/USD、MATIC/USD 等上百种;仅需替换合约 AggregatorV3Interface 地址并同步修改前端提示。
Q6:合约里的价格精度为什么有 8 位 0?
A:Chainlink 统一使用 8 位 decimal,便于回退到 int256 后仍保留小数点后两位。前端 /1e8 还原即可。
结语
至此,你已做完 智能合约开发 → 测试网部署 → 前端联调 的完整闭环。这套思路可复用到游戏、NFT、DeFi 借贷或任何需要链上数据的场景。下一步,不妨挑战:
- 将 数据存储 由变量升级为链上 事件日志,方便外部索引
- 添加 前端多网络切换,让用户一键体验不同区块链
- 引入 nextjs-boilerplate、Wagmi 等上层框架,节省 80% 样板代码
继续冲吧,Web3 开发者 的大门已为你敞开!