零基础也能开发 dApp:从智能合约到前端部署的 3 步全纪录

·

在 Web3 世界里,去中心化应用(dApp) 不只是区块链的代名词,更是人人都能上手的创新工具。本文将以“实时获取、存储 ETH / USD 价格”这一实际场景为例,手把手带你跑完 去中心化应用开发 全流程:编写智能合约 → 部署测试网 → 构建 React 前端。只需 3 步,让你从 0 进阶到可上线的 dApp!

悄悄告诉你:👉 点击这里,领取额外的实战技巧和隐藏合约模板


步骤 1|创建智能合约:用 Chainlink 喂价链上“记账”

1.1 技术选型 & 环境准备

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,复制并精简官方喂价合约示例。保留两个关键点:

最终合约代码(删去冗余注释后):

// 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 dotenv

2.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.svgApp.cssApp.test.js 等,轻装上阵。

3.2 App.js 核心实现

  1. 导入库

    import React, { useEffect, useState } from 'react';
    import { ethers } from 'ethers';
    import 'bootstrap/dist/css/bootstrap.min.css';
  2. 定义配置

    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 借贷或任何需要链上数据的场景。下一步,不妨挑战:

继续冲吧,Web3 开发者 的大门已为你敞开!