欢迎光临
我们一直在努力

二、区块链后端CrowdFund.sol与deploy.js

​ 一. CrowdFund.sol(智能合约)

功能:定义业务逻辑和状态管理

关键内容:

  • 合约状态变量(admin、paused、campaigns 等)

  • 业务函数(createCampaign、contribute、withdraw、refund 等)

  • 事件定义(CampaignCreated、Contributed 等)

  • 错误定义(Paused、NotAdmin 等)

编译输出:

  • 编译后生成 CrowdFund.json和contract-address.json

执行流程:

  • 读取编译后的合约 Artifact

  • 获取部署账户(Signer)

  • 创建合约工厂并部署

  • 等待部署确认

  • 获取部署信息(地址、区块号、时间戳)

  • 调用 exportFrontendArtifacts() 生成配置文件

  • 输出部署结果

  • 智能合约关键代码详解

    1、function createCampaign

    • owner:项目发起人(payable)
    • title:标题
    • description:描述
    • goal:目标金额(wei)
    • deadline:Unix 秒
    • raised:当前项目已筹(wei)
    • claimed:是否已提取

    function createCampaign(
    string memory title,
    string memory description,
    uint256 goal,
    uint256 deadline
    ) external whenNotPaused returns (uint256 campaignId) {
    if (goal == 0) revert InvalidGoal();
    if (deadline <= block.timestamp) revert InvalidDeadline();

    campaigns.push(
    Campaign({
    owner: payable(msg.sender),
    title: title,
    description: description,
    goal: goal,
    deadline: deadline,
    raised: 0,
    claimed: false
    })
    );
    campaignId = campaigns.length – 1;
    emit CampaignCreated(campaignId, msg.sender, title, goal, deadline);
    }

    external: 可被任何外部账户调用。
    whenNotPaused :确保全局未暂停,否则直接 Paused()。

    2、contribute、withdraw、refund 捐款、提款、退款

    function contribute(uint256 campaignId) external payable whenNotPaused {
    Campaign storage c = _campaign(campaignId);
    if (block.timestamp >= c.deadline) revert DeadlinePassed();
    require(msg.value > 0, "No ETH sent");
    c.raised += msg.value;
    totalRaised += msg.value;
    contributions[campaignId][msg.sender] += msg.value;
    emit Contributed(campaignId, msg.sender, msg.value);
    }

    function withdraw(uint256 campaignId) external nonReentrant whenNotPaused {//提款
    Campaign storage c = _campaign(campaignId);
    if (msg.sender != c.owner) revert NotOwner();
    if (block.timestamp < c.deadline) revert DeadlineNotReached();
    if (c.raised < c.goal) revert GoalNotReached();
    if (c.claimed) revert AlreadyClaimed();
    c.claimed = true;
    uint256 amount = c.raised;
    (bool ok, ) = c.owner.call{value: amount}("");
    require(ok, "Transfer failed");
    emit Withdrawn(campaignId, c.owner, amount);
    }

    function refund(uint256 campaignId) external nonReentrant whenNotPaused {//退款
    Campaign storage c = _campaign(campaignId);
    if (block.timestamp < c.deadline) revert DeadlineNotReached();
    if (c.raised >= c.goal) revert GoalNotReached();
    uint256 amount = contributions[campaignId][msg.sender];
    if (amount == 0) revert NothingToRefund();
    contributions[campaignId][msg.sender] = 0;
    (bool ok, ) = payable(msg.sender).call{value: amount}("");
    require(ok, "Refund failed");
    emit Refunded(campaignId, msg.sender, amount);
    }

    -contribute(uint256 campaignId) external payable whenNotPaused

    • 项目必须存在

    • block.timestamp < c.deadline,否则 DeadlinePassed()

    • msg.value > 0,否则 require(“No ETH sent”)

    • 状态更新:c.raised += msg.value、totalRaised += msg.value、contributions[campaignId][msg.sender] += msg.value

      关键点:允许超额筹集;totalRaised 为平台累计指标,不与退款联动减少。前端提示用户退款规则依赖目标达成与截止时间状态。

    -withdraw(uint256 campaignId) external nonReentrant whenNotPaused

    • 用途:发起人在达成目标且到期后提取资金。
      • 项目存在,调用者 msg.sender == c.owner,否则 NotOwner()
      • block.timestamp >= c.deadline,否则 DeadlineNotReached()
      • c.raised >= c.goal,否则 GoalNotReached()

    -refund(uint256 campaignId) external nonReentrant whenNotPaused

    • 用途:未达成目标且到期后,出资人退款。
      • 项目存在
      • block.timestamp >= c.deadline,否则 DeadlineNotReached()
      • c.raised < c.goal,否则 GoalNotReached()
      • 个人出资额 amount > 0,否则 NothingToRefund()
      • 状态更新:先将 contributions[campaignId][msg.sender] = 0

    注意点:为什么退款先清零再转账

    • 防止重入攻击:如果先转账再清零,恶意合约可能在 call 时回调 refund

    • 第二次调用时,contributions 仍为非零值,可能导致重复退款

    deploy.js详解

    async function main()

    入口函数,负责:获取部署者、部署合约、收集部署元数据、导出前端所需文件、打印提示信息。

    const [deployer] = await ethers.getSigners();
    console.log("Deploying CrowdFund with account:", deployer.address);

    • 含义:从当前网络配置(hardhat.config.js)读取可用账户,拿accounts第一个元素作为部署者。
    • 作用:打印部署者地址,便于溯源和审计。

    const CrowdFund = await ethers.getContractFactory("CrowdFund");
    const contract = await CrowdFund.deploy();
    await contract.waitForDeployment();

    • 含义:
      • getContractFactory("CrowdFund") 从编译产物(artifacts)加载 CrowdFund 的工厂实例。
      • deploy() 发送部署交易;waitForDeployment() 阻塞直到部署合约在链上可用(已被打包确认)。
    • 边界:若未先 npx hardhat compile,工厂加载会失败。

    const address = await contract.getAddress();
    const tx = await contract.deploymentTransaction().wait();
    const deploymentBlock = tx.blockNumber;
    const deploymentTimestamp = (await ethers.provider.getBlock(tx.blockNumber)).timestamp;

    • 含义:
      • getAddress() 获取合约部署地址。
      • deploymentTransaction() 返回部署时的交易对象,wait() 得到收据(包括区块号)。
      • 从区块读取 timestamp 记录部署时间。

    赞(0)
    未经允许不得转载:171主机测评 » 二、区块链后端CrowdFund.sol与deploy.js
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址