:2026-03-09 6:57 点击:1
在动手开发前,需先理解以太坊App(DApp)的核心逻辑:“前端+智能合约+区块链交互”的三层架构。
node -v检查)。 以简单“投票DApp”为例,合约需实现:
// contracts/Voting.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Voting {
mapping(address => bool) public hasVoted;
mapping(string => uint256) public voteCount;
string[] public candidates;
constructor(string[] memory _candidates) {
candidates = _candidates;
}
function vote(string memory candidate) public {
require(!hasVoted[msg.sender], "You have already voted!");
require(_isValidCandidate(candidate), "Invalid candidate!");
hasVoted[msg.sender] = true;
voteCount[candidate]++;
}
function _i
sValidCandidate(string memory candidate) private view returns (bool) {
for (uint i = 0; i < candidates.length; i++) {
if (keccak256(bytes(candidates[i])) == keccak256(bytes(candidate))) {
return true;
}
}
return false;
}
function getCandidates() public view returns (string[] memory) {
return candidates;
}
}
初始化Hardhat项目:
mkdir voting-dapp && cd voting-dapp npm init -y npm install --save-dev hardhat npx hardhat # 选择"Create a basic sample project",默认配置
添加合约文件:将上述Voting.sol放入contracts/目录。
编写测试脚本(test/voting.test.js):
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Voting", function () {
let voting;
const candidates = ["Alice", "Bob"];
beforeEach(async function () {
const Voting = await ethers.getContractFactory("Voting");
voting = await Voting.deploy(candidates);
await voting.deployed();
});
it("Should initialize with candidates", async function () {
const retrievedCandidates = await voting.getCandidates();
expect(retrievedCandidates).to.deep.equal(candidates);
});
it("Should allow voting", async function () {
await voting.vote("Alice");
const voteCount = await voting.voteCount("Alice");
expect(voteCount).to.equal(1);
});
it("Should prevent double voting", async function () {
await voting.vote("Alice");
await expect(voting.vote("Alice")).to.be.revertedWith("You have already voted!");
});
});
运行测试:
npx hardhat test
配置网络信息:在hardhat.config.js中添加测试网配置(以Sepolia为例):
require("@nomicfoundation/hardhat-toolbox");
require('dotenv').config();
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.0",
networks: {
sepolia: {
url: process.env.SEPOLIA_URL, // 从Infura/Alchemy获取
accounts: [process.env.PRIVATE_KEY], // 测试账户私钥(需.env文件管理)
},
},
};
创建部署脚本(scripts/deploy.js):
async function main() {
const Voting = await ethers.getContractFactory("Voting");
const voting = await Voting.deploy(["Alice", "Bob"]);
await voting.deployed();
console.log("Voting contract deployed to:", voting.address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
部署到测试网:
npx hardhat run scripts/deploy.js --network sepolia
部署成功后,合约地址会显示在控制台,可复制到Etherscan测试网查看。
安装前端依赖:
npm install react react-dom @ethersproject/providers ethers
创建React应用(src/App.js):
import { useState, useEffect } from 'react';
import { ethers } from 'ethers';
function App() {
const [contract, setContract] = useState(null);
const [candidates, setCandidates] = useState([]);
const [voteCounts, setVoteCounts] = useState({});
const [selectedCandidate, setSelectedCandidate] = useState('');
const [account, setAccount] = useState('');
// 初始化:连接钱包与合约
useEffect(() => {
const init = async () => {
// 连接MetaMask
if (window.ethereum) {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
setAccount(accounts[0]);
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
// 部署合约地址(替换为实际部署地址)
const votingAddress = "0xYourContractAddress";
const votingContract = new ethers.Contract(votingAddress, [
"function getCandidates() view returns (string[])",
"function vote(string)",
"function voteCount(string) view returns (uint256)"
], signer);
setContract(votingContract);
// 获取候选人数据
const candidatesList = await votingContract.getCandidates();
setCandidates(candidatesList);
const counts = {};
for (const candidate of candidatesList) {
counts[candidate] = (await votingContract.voteCount(candidate)).toString();
}
setVoteCounts(counts);
}
};
init();
}, []);
// 投票函数
const handleVote = async () => {
if (!selectedCandidate || !contract) return;
try {
const tx = await contract.vote(selectedCandidate);
await tx.wait();
alert("投票成功!");
// 刷新投票数据
const newCounts = { ...voteCounts };
newCounts[selectedCandidate] = (parseInt(newCounts[selectedCandidate]) + 1).toString();
setVoteCounts(newCounts);
} catch (error) {
console.error(error);
alert("投票失败:" + error.message);
}
};
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h1>以太坊投票DApp</h1>
<p>当前账户: {account}</p>
<h2>候选人列表</h2>
<ul>
{candidates.map((candidate, index) => (
<li key={index}>
{candidate} - 票数: {voteCounts[candidate] || 0}
<input
type="radio"
name="candidate
本文由用户投稿上传,若侵权请提供版权资料并联系删除!