DEV Community

richard202605
richard202605

Posted on

Web3 开发者入门完全指南:从零构建你的第一个 DApp

Web3 开发者入门完全指南:从零构建你的第一个 DApp

想进入 Web3 开发但不知道从哪开始?这篇指南带你从零搭建开发环境,写第一个智能合约,部署到测试网,并用前端连接它。


为什么学 Web3 开发?

  • 💰 Web3 开发者薪资普遍高于传统开发者
  • 🌍 去中心化应用正在重塑金融、游戏、社交
  • 🔧 工具链已经成熟,入门门槛大幅降低
  • 📈 2026 年 Web3 岗位需求持续增长

1. 开发环境搭建

1.1 安装 Node.js

# 使用 nvm 安装(推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 18
nvm use 18

# 验证
node --version  # v18.x.x
npm --version   # 9.x.x
Enter fullscreen mode Exit fullscreen mode

1.2 安装开发框架

Hardhat(推荐新手):

mkdir my-first-dapp && cd my-first-dapp
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init
Enter fullscreen mode Exit fullscreen mode

选择 "Create a JavaScript project",其余默认即可。

项目结构:

my-first-dapp/
├── contracts/          # 智能合约
├── scripts/            # 部署脚本
├── test/               # 测试文件
├── hardhat.config.js   # 配置文件
└── package.json
Enter fullscreen mode Exit fullscreen mode

1.3 安装 MetaMask

  1. 访问 https://metamask.io
  2. 安装浏览器扩展
  3. 创建钱包(保管好助记词!
  4. 添加 Sepolia 测试网

2. Solidity 基础

Solidity 是以太坊的智能合约语言,语法类似 JavaScript。

2.1 Hello World 合约

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract HelloWorld {
    string public greeting;
    address public owner;

    constructor() {
        greeting = "Hello, Web3!";
        owner = msg.sender;
    }

    function setGreeting(string memory _greeting) public {
        require(msg.sender == owner, "Only owner can change greeting");
        greeting = _greeting;
    }

    function getGreeting() public view returns (string memory) {
        return greeting;
    }
}
Enter fullscreen mode Exit fullscreen mode

2.2 核心概念

概念 说明
pragma 指定编译器版本
contract 类似 class,是合约的基本单位
public 自动生成 getter 函数
view 只读函数,不修改状态
msg.sender 调用者的地址
constructor 部署时执行一次

2.3 数据类型

// 基础类型
uint256 public number = 42;        // 无符号整数
bool public active = true;          // 布尔值
address public addr;                // 以太坊地址
string public name = "FlowForge";   // 字符串

// 映射(类似字典)
mapping(address => uint256) public balances;

// 数组
uint256[] public numbers;
Enter fullscreen mode Exit fullscreen mode

3. 编写第一个完整合约

让我们创建一个简单的投票合约:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract SimpleVoting {
    // 数据结构
    struct Proposal {
        string name;
        uint256 voteCount;
    }

    // 状态变量
    address public chairperson;
    mapping(address => bool) public hasVoted;
    Proposal[] public proposals;

    // 事件
    event Voted(address indexed voter, uint256 proposalIndex);

    // 构造函数
    constructor(string[] memory proposalNames) {
        chairperson = msg.sender;
        for (uint256 i = 0; i < proposalNames.length; i++) {
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
        }
    }

    // 投票函数
    function vote(uint256 proposalIndex) public {
        require(!hasVoted[msg.sender], "Already voted");
        require(proposalIndex < proposals.length, "Invalid proposal");

        hasVoted[msg.sender] = true;
        proposals[proposalIndex].voteCount++;

        emit Voted(msg.sender, proposalIndex);
    }

    // 获取提案数量
    function getProposalCount() public view returns (uint256) {
        return proposals.length;
    }

    // 获取提案信息
    function getProposal(uint256 index) public view returns (string memory name, uint256 voteCount) {
        require(index < proposals.length, "Invalid index");
        Proposal storage p = proposals[index];
        return (p.name, p.voteCount);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. 编写测试

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("SimpleVoting", function () {
  let voting;
  let owner, voter1, voter2;

  beforeEach(async function () {
    [owner, voter1, voter2] = await ethers.getSigners();
    const SimpleVoting = await ethers.getContractFactory("SimpleVoting");
    voting = await SimpleVoting.deploy(["Alice", "Bob", "Charlie"]);
  });

  it("Should initialize proposals", async function () {
    expect(await voting.getProposalCount()).to.equal(3);

    const [name, votes] = await voting.getProposal(0);
    expect(name).to.equal("Alice");
    expect(votes).to.equal(0);
  });

  it("Should allow voting", async function () {
    await voting.connect(voter1).vote(0);

    const [, votes] = await voting.getProposal(0);
    expect(votes).to.equal(1);
  });

  it("Should prevent double voting", async function () {
    await voting.connect(voter1).vote(0);
    await expect(voting.connect(voter1).vote(1)).to.be.revertedWith("Already voted");
  });
});
Enter fullscreen mode Exit fullscreen mode

运行测试:

npx hardhat test
Enter fullscreen mode Exit fullscreen mode

5. 部署到测试网

5.1 配置 Sepolia 网络

// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: "0.8.20",
  networks: {
    sepolia: {
      url: "https://ethereum-sepolia-rpc.publicnode.com",
      accounts: ["YOUR_PRIVATE_KEY"]  // MetaMask 导出的私钥
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

5.2 编写部署脚本

// scripts/deploy.js
const hre = require("hardhat");

async function main() {
  const SimpleVoting = await hre.ethers.getContractFactory("SimpleVoting");
  const voting = await SimpleVoting.deploy(["Alice", "Bob", "Charlie"]);

  await voting.waitForDeployment();
  console.log("SimpleVoting deployed to:", await voting.getAddress());
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
Enter fullscreen mode Exit fullscreen mode

5.3 执行部署

# 确保钱包有测试 ETH
npx hardhat run scripts/deploy.js --network sepolia
Enter fullscreen mode Exit fullscreen mode

6. 前端连接钱包

6.1 安装 ethers.js

npm install ethers
Enter fullscreen mode Exit fullscreen mode

6.2 连接 MetaMask

import { ethers } from "ethers";

// 连接钱包
async function connectWallet() {
  if (typeof window.ethereum === "undefined") {
    alert("请安装 MetaMask!");
    return;
  }

  const provider = new ethers.BrowserProvider(window.ethereum);
  const signer = await provider.getSigner();
  const address = await signer.getAddress();

  console.log("连接成功:", address);
  return { provider, signer };
}
Enter fullscreen mode Exit fullscreen mode

6.3 与合约交互

// 合约 ABI(编译后生成)
const CONTRACT_ABI = [...]; // 从 artifacts/contracts/SimpleVoting.sol/SimpleVoting.json 复制
const CONTRACT_ADDRESS = "0x..."; // 部署后的合约地址

async function vote(proposalIndex) {
  const { signer } = await connectWallet();
  const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, signer);

  const tx = await contract.vote(proposalIndex);
  await tx.wait();

  console.log("投票成功!");
}

async function getProposals() {
  const { provider } = await connectWallet();
  const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider);

  const count = await contract.getProposalCount();
  const proposals = [];

  for (let i = 0; i < count; i++) {
    const [name, votes] = await contract.getProposal(i);
    proposals.push({ name, votes: Number(votes) });
  }

  return proposals;
}
Enter fullscreen mode Exit fullscreen mode

7. 安全最佳实践

7.1 常见漏洞

漏洞 说明 防御
重入攻击 外部调用中重复进入函数 使用 ReentrancyGuard
整数溢出 数值超出范围 Solidity 0.8+ 自动检查
权限漏洞 未限制关键函数 使用 modifier 验证
前端攻击 用户签署恶意交易 验证交易参数

7.2 安全检查清单

  • [ ] 使用最新 Solidity 版本
  • [ ] 所有外部调用放在最后
  • [ ] 关键函数添加权限控制
  • [ ] 使用 OpenZeppelin 库
  • [ ] 充分测试边界情况
  • [ ] 部署前进行审计

8. 进阶学习路线

初级 → 中级 → 高级
│       │       │
│       │       └── 形式化验证、MEV、跨链
│       │
│       └── DeFi 协议、NFT、DAO
│
└── Solidity 基础、ERC-20、测试
Enter fullscreen mode Exit fullscreen mode

推荐资源:


总结

你已经学会了:

  1. ✅ 搭建 Hardhat 开发环境
  2. ✅ 编写 Solidity 智能合约
  3. ✅ 编写和运行测试
  4. ✅ 部署到 Sepolia 测试网
  5. ✅ 用前端连接钱包和合约

下一步: 尝试构建一个完整的 DApp,比如 NFT 铸造、DeFi 借贷、或去中心化投票系统。


📌 免责声明: 本文仅供教育目的。智能合约涉及真金白银,请充分测试后再部署到主网。


觉得有帮助?关注 @richard202605 获取更多 Web3 开发教程。有问题?评论区见!

Top comments (0)