本文作者:咔咔

区块链Basic项目如何实现技术落地与生态协同?

区块链Basic项目如何实现技术落地与生态协同?摘要: 核心功能明确:只专注于区块链最核心的1-2个功能,避免功能堆砌,技术栈清晰:使用成熟、主流的开发框架,便于学习和上手,可运行和验证:能够成功部署并看到效果,这是保持学习动力的关键,...
  • 核心功能明确:只专注于区块链最核心的1-2个功能,避免功能堆砌。
  • 技术栈清晰:使用成熟、主流的开发框架,便于学习和上手。
  • 可运行和验证:能够成功部署并看到效果,这是保持学习动力的关键。

下面我将为你提供几个不同难度和方向的“区块链Basic项目”构想,并详细阐述其中一个最经典、最适合入门的项目——去中心化投票系统


项目构想清单(从易到难)

这里有几个不同方向的项目点子,你可以根据自己的兴趣和技术背景选择:

区块链Basic项目如何实现技术落地与生态协同?
(图片来源网络,侵删)

入门级:概念验证类

这类项目旨在理解区块链的核心概念,如交易、区块、挖矿和代币。

  • 项目名称:极简代币发行
  • 核心功能
    • 创建一个自己的代币(MyFirstToken)。
    • 实现一个简单的合约,可以向指定地址发行代币。
    • 实现一个简单的合约,可以查询某个地址的代币余额。
  • 学习重点:理解智能合约的部署、函数调用、事件、地址和基础数据类型。
  • 技术栈:Solidity, Hardhat/Truffle, Remix IDE, 一个测试网(如 Sepolia)。

进阶级:实用工具类

这类项目开始引入真实世界的应用场景,涉及状态管理、用户交互和安全性。

  • 项目名称:去中心化投票系统
  • 核心功能
    • 创建投票合约,可以发起一个投票(“你更喜欢猫还是狗?”)。
    • 只有拥有投票权的人(白名单地址或代币持有者)才能投票。
    • 每个地址只能投一次票。
    • 投票结束后,可以查询最终的投票结果。
  • 学习重点:状态变量、函数修饰符(modifier)、数组/映射的使用、访问控制、事件记录。
  • 技术栈:Solidity, Hardhat/Truffle, Web3.js 或 Ethers.js (用于前端交互), 测试网。

中等级:游戏/金融类

这类项目引入了更复杂的逻辑,如状态机、代币经济学和金融操作。

  • 项目名称:简单版去中心化彩票
  • 核心功能
    • 用户可以投入少量ETH参与抽奖。
    • 设置一个开奖时间(24小时后)。
    • 开奖时,使用一个不可预测的值(如上一个区块的哈希)作为随机数,选出获奖者。
    • 获奖者获得奖池中的所有ETH。
  • 学习重点:安全随机数的获取(这是一个难点)、时间控制、ETH的转账、合约余额管理。
  • 技术栈:Solidity, Hardhat/Truffle, Web3.js/Ethers.js, 测试网。

中高级:NFT类

这是目前最热门的方向,结合了艺术、所有权和社区。

区块链Basic项目如何实现技术落地与生态协同?
(图片来源网络,侵删)
  • 项目名称:生成式艺术NFT
  • 核心功能
    • 创建一个NFT合约,可以铸造NFT。
    • NFT的图像不是固定的,而是根据其tokenId通过算法(如生成程序化艺术)动态生成的。
    • 用户支付一定费用(Minting Fee)即可铸造属于自己的独特NFT。
  • 学习重点:NFT标准(ERC-721 或 ERC-1155)、元数据、链下存储(如IPFS)、哈希算法、前端Canvas绘图。
  • 技术栈:Solidity, Hardhat/Truffle, IPFS, Web3.js/Ethers.js, React/Vue.js, Canvas API。

详细项目教程:去中心化投票系统

我们选择 项目2:去中心化投票系统 作为详细讲解的例子,因为它完美地平衡了学习价值和实现难度。

项目目标

创建一个基于以太坊智能合约的投票系统,确保投票过程的透明、公正且不可篡改。

技术栈

  • 智能合约Solidity (语言), Hardhat (开发框架)
  • 前端HTML, CSS, JavaScript (为了简单,我们先不用框架)
  • 交互库Ethers.js (用于前端与区块链通信)
  • 测试网络Sepolia (一个公共测试网)
  • 钱包MetaMask (浏览器插件钱包)

开发步骤

第1步:环境搭建

  1. 安装 Node.js 和 npm: 确保你的电脑上已安装。
  2. 安装 MetaMask: 浏览器插件商店中安装,并创建一个钱包。
  3. 获取测试网ETH: 前往 Sepolia Faucet (或其他水龙头),为你的MetaMask钱包领取一些测试用的ETH。
  4. 初始化Hardhat项目:
    mkdir blockchain-voting
    cd blockchain-voting
    npm init -y
    npm install --save-dev hardhat
    npx hardhat

    在交互式安装中,选择 "Create a basic sample project"。

第2步:编写智能合约

  1. contracts/ 目录下,创建一个新文件 Voting.sol
  2. 编写以下合约代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// 投票选项的结构体
struct Voter {
    bool isVoted;   // 是否已投票
    uint8 voteIndex; // 投给了哪个选项的索引
}
contract Voting {
    // 投票选项的数组
    string[] public proposals;
    // 记录每个投票者的状态
    // address => Voter
    mapping(address => Voter) public voters;
    // 记录每个选项获得的票数
    mapping(uint8 => uint256) public voteCount;
    // 事件:当有人投票时触发,方便前端监听
    event Voted(address voter, uint8 proposalIndex);
    // 构造函数,在部署合约时调用,用于初始化投票选项
    constructor(string[] memory _proposals) {
        proposals = _proposals;
    }
    // 修饰符:确保调用者有投票权且尚未投票
    modifier canVote() {
        require(!voters[msg.sender].isVoted, "You have already voted.");
        _;
    }
    // 投票函数
    function vote(uint8 _proposalIndex) public canVote {
        // 检查投票选项是否有效
        require(_proposalIndex < proposals.length, "Invalid proposal index.");
        // 更新投票者状态
        voters[msg.sender].isVoted = true;
        voters[msg.sender].voteIndex = _proposalIndex;
        // 增加对应选项的票数
        voteCount[_proposalIndex]++;
        // 触发事件
        emit Voted(msg.sender, _proposalIndex);
    }
    // 获取所有投票选项
    function getProposals() public view returns (string[] memory) {
        return proposals;
    }
    // 获取总票数
    function getTotalVotes() public view returns (uint256) {
        uint256 total = 0;
        for (uint i = 0; i < proposals.length; i++) {
            total += voteCount[i];
        }
        return total;
    }
}

代码解析:

区块链Basic项目如何实现技术落地与生态协同?
(图片来源网络,侵删)
  • struct Voter: 定义了一个结构体来存储每个投票者的信息。
  • string[] public proposals: 一个公共的字符串数组,存储所有投票选项(如 "Option 1", "Option 2")。
  • mapping(address => Voter) public voters: 一个关键的映射,将每个钱包地址与一个 Voter 结构体关联起来,用来记录谁投了票,投给了谁。
  • mapping(uint8 => uint256) public voteCount: 另一个关键映射,记录每个选项的得票数。
  • constructor: 合约部署时运行,用于初始化投票选项。
  • vote(uint8 _proposalIndex): 核心投票函数。canVote 修饰符确保了投票的合法性。
  • require: 用于检查条件,如果不满足则回滚交易并报错。
  • event Voted: 定义了一个事件,前端可以监听这个事件来实时更新UI。

第3步:编译和部署合约

  1. 配置Hardhat: 打开 hardhat.config.js,添加Sepolia网络的配置:

    require("@nomicfoundation/hardhat-toolbox");
    require('dotenv').config(); // 用于加载环境变量
    /** @type import('hardhat/config').HardhatUserConfig */
    module.exports = {
      solidity: "0.8.20",
      networks: {
        sepolia: {
          url: process.env.SEPOLIA_URL, // 从 .env 文件中读取
          accounts: [process.env.PRIVATE_KEY], // 从 .env 文件中读取
        },
      },
    };
  2. 创建 .env 文件: 在项目根目录创建,内容如下:

    SEPOLIA_URL="你的Alchemy或Infura的Sepolia节点URL"
    PRIVATE_KEY="你的MetaMask钱包的私钥(注意:不要泄露!)"
  3. 安装依赖:

    npm install dotenv @nomicfoundation/hardhat-toolbox
  4. 编写部署脚本: 在 scripts/ 目录下,创建 deploy.js

    async function main() {
      // 获取合约工厂
      const Voting = await ethers.getContractFactory("Voting");
      // 定义投票选项
      const proposals = ["Proposal 1", "Proposal 2", "Proposal 3"];
      // 部署合约
      const voting = await Voting.deploy(proposals);
      await voting.waitForDeployment();
      // 输出合约地址
      console.log("Voting contract deployed to:", await voting.getAddress());
    }
    main().catch((error) => {
      console.error(error);
      process.exitCode = 1;
    });
  5. 部署到测试网:

    npx hardhat run scripts/deploy.js --network sepolia

    成功后,你会得到一个合约地址,记下来!

第4步:创建前端界面

  1. 在项目根目录创建 index.html, style.css, script.js 文件。

  2. index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Blockchain Voting</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <h1>Decentralized Voting</h1>
        <div id="app">
            <div id="proposals"></div>
            <button id="connect-wallet">Connect Wallet</button>
            <button id="get-results" style="display:none;">Get Results</button>
            <div id="results"></div>
        </div>
        <script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js" type="application/javascript"></script>
        <script src="script.js"></script>
    </body>
    </html>
  3. style.css (简单美化):

    body { font-family: sans-serif; text-align: center; }
    #proposals { margin: 20px; }
    .proposal-item { margin: 10px; }
    button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
    #results { margin-top: 20px; }
  4. script.js (核心交互逻辑):

    let contract;
    let signer;
    // 替换成你部署的合约地址
    const contractAddress = "0x...你的合约地址...";
    // ABI (Application Binary Interface) 是合约的接口描述
    // 你可以从编译后的 artifacts/contracts/Voting.sol/Voting.json 中复制
    const contractABI = [
        // ... (这里粘贴完整的ABI,为了简洁,省略了)
        "function vote(uint8 _proposalIndex) external",
        "function getProposals() external view returns (string[] memory)",
        "function getTotalVotes() external view returns (uint256)",
        "function voteCount(uint8) external view returns (uint256)"
    ];
    const connectWallet = async () => {
        if (window.ethereum) {
            try {
                const provider = new ethers.BrowserProvider(window.ethereum);
                signer = await provider.getSigner();
                contract = new ethers.Contract(contractAddress, contractABI, signer);
                document.getElementById("connect-wallet").innerText = "Wallet Connected: " + await signer.getAddress();
                document.getElementById("connect-wallet").disabled = true;
                document.getElementById("get-results").style.display = "block";
                loadProposals();
            } catch (err) {
                console.error(err);
            }
        } else {
            alert("Please install MetaMask!");
        }
    };
    const loadProposals = async () => {
        const proposals = await contract.getProposals();
        const proposalsDiv = document.getElementById("proposals");
        proposalsDiv.innerHTML = "<h2>Cast Your Vote:</h2>";
        for (let i = 0; i < proposals.length; i++) {
            const proposalItem = document.createElement("div");
            proposalItem.className = "proposal-item";
            const label = document.createElement("label");
            label.innerHTML = `<input type="radio" name="vote" value="${i}"> ${proposals[i]}`;
            const voteButton = document.createElement("button");
            voteButton.innerText = "Vote";
            voteButton.onclick = () => vote(i);
            proposalItem.appendChild(label);
            proposalItem.appendChild(voteButton);
            proposalsDiv.appendChild(proposalItem);
        }
    };
    const vote = async (proposalIndex) => {
        try {
            const tx = await contract.vote(proposalIndex);
            await tx.wait();
            alert("Vote cast successfully!");
            loadProposals(); // 重新加载以更新状态
        } catch (error) {
            console.error(error);
            alert("Voting failed. Check console for details.");
        }
    };
    const getResults = async () => {
        const proposals = await contract.getProponals();
        const resultsDiv = document.getElementById("results");
        resultsDiv.innerHTML = "<h2>Results:</h2>";
        for (let i = 0; i < proposals.length; i++) {
            const count = await contract.voteCount(i);
            const resultItem = document.createElement("p");
            resultItem.innerText = `${proposals[i]}: ${count} votes`;
            resultsDiv.appendChild(resultItem);
        }
    };
    document.getElementById("connect-wallet").onclick = connectWallet;
    document.getElementById("get-results").onclick = getResults;

第5步:运行和测试

  1. package.json 中添加一个启动脚本:
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    // 投票选项的结构体
    struct Voter {
        bool isVoted;   // 是否已投票
        uint8 voteIndex; // 投给了哪个选项的索引
    }
    contract Voting {
        // 投票选项的数组
        string[] public proposals;
        // 记录每个投票者的状态
        // address => Voter
        mapping(address => Voter) public voters;
        // 记录每个选项获得的票数
        mapping(uint8 => uint256) public voteCount;
        // 事件:当有人投票时触发,方便前端监听
        event Voted(address voter, uint8 proposalIndex);
        // 构造函数,在部署合约时调用,用于初始化投票选项
        constructor(string[] memory _proposals) {
            proposals = _proposals;
        }
        // 修饰符:确保调用者有投票权且尚未投票
        modifier canVote() {
            require(!voters[msg.sender].isVoted, "You have already voted.");
            _;
        }
        // 投票函数
        function vote(uint8 _proposalIndex) public canVote {
            // 检查投票选项是否有效
            require(_proposalIndex < proposals.length, "Invalid proposal index.");
            // 更新投票者状态
            voters[msg.sender].isVoted = true;
            voters[msg.sender].voteIndex = _proposalIndex;
            // 增加对应选项的票数
            voteCount[_proposalIndex]++;
            // 触发事件
            emit Voted(msg.sender, _proposalIndex);
        }
        // 获取所有投票选项
        function getProposals() public view returns (string[] memory) {
            return proposals;
        }
        // 获取总票数
        function getTotalVotes() public view returns (uint256) {
            uint256 total = 0;
            for (uint i = 0; i < proposals.length; i++) {
                total += voteCount[i];
            }
            return total;
        }
    }0
  2. 安装 live-server 并启动前端:
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    // 投票选项的结构体
    struct Voter {
        bool isVoted;   // 是否已投票
        uint8 voteIndex; // 投给了哪个选项的索引
    }
    contract Voting {
        // 投票选项的数组
        string[] public proposals;
        // 记录每个投票者的状态
        // address => Voter
        mapping(address => Voter) public voters;
        // 记录每个选项获得的票数
        mapping(uint8 => uint256) public voteCount;
        // 事件:当有人投票时触发,方便前端监听
        event Voted(address voter, uint8 proposalIndex);
        // 构造函数,在部署合约时调用,用于初始化投票选项
        constructor(string[] memory _proposals) {
            proposals = _proposals;
        }
        // 修饰符:确保调用者有投票权且尚未投票
        modifier canVote() {
            require(!voters[msg.sender].isVoted, "You have already voted.");
            _;
        }
        // 投票函数
        function vote(uint8 _proposalIndex) public canVote {
            // 检查投票选项是否有效
            require(_proposalIndex < proposals.length, "Invalid proposal index.");
            // 更新投票者状态
            voters[msg.sender].isVoted = true;
            voters[msg.sender].voteIndex = _proposalIndex;
            // 增加对应选项的票数
            voteCount[_proposalIndex]++;
            // 触发事件
            emit Voted(msg.sender, _proposalIndex);
        }
        // 获取所有投票选项
        function getProposals() public view returns (string[] memory) {
            return proposals;
        }
        // 获取总票数
        function getTotalVotes() public view returns (uint256) {
            uint256 total = 0;
            for (uint i = 0; i < proposals.length; i++) {
                total += voteCount[i];
            }
            return total;
        }
    }1
  3. 在浏览器中打开 http://127.0.0.1:8080
  4. 点击 "Connect Wallet" 按钮,连接你的MetaMask。
  5. 你会看到投票选项,选择一个并点击 "Vote"。
  6. 在MetaMask中确认交易,交易成功后,页面会更新,你将无法再次投票。
  7. 点击 "Get Results" 查看实时投票结果。

总结与后续学习

恭喜!你已经成功完成了一个基础的区块链项目,这个项目涵盖了智能合约开发、编译、部署、前后端交互和测试的全过程。

后续可以深入探索的方向:

  1. 安全性:学习常见的智能合约漏洞(如重入攻击、整数溢出等),并使用 SlitherMythX 等工具进行审计。
  2. 前端框架:使用 ReactVue.js 重构前端,获得更好的用户体验和组件化开发体验。
  3. 去中心化存储:将投票结果或元数据存储在 IPFS 上,实现真正的去中心化。
  4. 更复杂的DApp:尝试构建一个去中心化交易所、借贷协议或更复杂的NFT项目。

从Basic项目开始,逐步增加复杂度,是学习区块链开发最有效的方式,祝你学习顺利!

文章版权及转载声明

作者:咔咔本文地址:https://jits.cn/content/29117.html发布于 昨天
文章转载或复制请以超链接形式并注明出处杰思科技・AI 股讯

阅读
分享

发表评论

快捷回复:

评论列表 (暂无评论,1人围观)参与讨论

还没有评论,来说两句吧...