solidity从入门到精通 第七章:高级特性与实战项目

发布于:2025-09-05 ⋅ 阅读:(12) ⋅ 点赞:(0)

第七章:高级特性与实战项目

从学徒到大师:高级Solidity之旅

欢迎来到我们Solidity之旅的最后一章,勇敢的区块链探险家!如果你一路跟随我们到这里,恭喜你——你已经从一个区块链新手成长为一个有能力构建智能合约的开发者。就像从"你好,世界"到"我可以创建自己的数字经济",这是一段令人印象深刻的旅程。

在本章中,我们将探索Solidity的高级特性,学习专业开发者使用的设计模式,并通过一个完整的实战项目将我们所学的知识整合起来。准备好迎接终极挑战了吗?让我们开始吧!

Solidity高级特性:解锁更强大的工具

1. 库(Libraries)

库是一种特殊类型的合约,主要用于代码重用。与普通合约不同,库不能有状态变量,不能继承或被继承,也不能接收以太币。

库的优势

  • 代码重用
  • 节省gas(库代码只部署一次)
  • 逻辑分离和模块化

创建和使用库

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

// 定义一个数学库
library MathLib {
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }
    
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
    
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // 防止溢出的安全平均值计算
        return (a & b) + (a ^ b) / 2;
    }
}

// 使用库的合约
contract MathUser {
    using MathLib for uint256;
    
    function testMax(uint256 a, uint256 b) public pure returns (uint256) {
        // 方法1:直接调用库函数
        return MathLib.max(a, b);
    }
    
    function testMin(uint256 a, uint256 b) public pure returns (uint256) {
        // 方法2:使用附加到类型的库函数
        return a.min(b);
    }
    
    function testAverage(uint256 a, uint256 b) public pure returns (uint256) {
        return a.average(b);
    }
}

using A for B语法将库A的函数附加到类型B上,使其可以像方法一样调用。

2. 接口(Interfaces)

接口定义了合约应该实现的函数,但不提供实现。它们类似于其他编程语言中的抽象类或接口。

接口的特点

  • 不能包含状态变量
  • 不能包含构造函数
  • 不能继承除接口外的其他合约
  • 所有函数必须是external且不能有实现

定义和实现接口

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

// 定义代币接口
interface IToken {
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

// 实现接口的合约
contract MyToken is IToken {
    mapping(address => uint256) private _balances;
    
    constructor() {
        _balances[msg.sender] = 1000000;
    }
    
    function transfer(address to, uint256 amount) external override returns (bool) {
        require(_balances[msg.sender] >= amount, "Insufficient balance");
        
        _balances[msg.sender] -= amount;
        _balances[to] += amount;
        
        return true;
    }
    
    function balanceOf(address account) external view override returns (uint256) {
        return _balances[account];
    }
}

// 使用接口的合约
contract TokenUser {
    function transferTokens(address tokenContract, address to, uint256 amount) public {
        IToken token = IToken(tokenContract);
        require(token.transfer(to, amount), "Transfer failed");
    }
    
    function checkBalance(address tokenContract, address account) public view returns (uint256) {
        return IToken(tokenContract).balanceOf(account);
    }
}

接口使合约能够与其他合约交互,而无需知道它们的完整实现。这促进了模块化和可组合性,这是区块链生态系统的关键特性。

3. 继承与多态性

Solidity支持多重继承,允许合约从多个父合约继承功能。

继承的特点

  • 代码重用
  • 逻辑扩展
  • 多态性(通过虚函数)

继承示例

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

// 基础合约
contract Ownable {
    address public owner;
    
    constructor() {
        owner = msg.sender;
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }
    
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "New owner cannot be zero address");
        owner = newOwner;
    }
}

// 基础代币功能
contract BasicToken {
    mapping(address => uint256) internal _balances;
    uint256 public totalSupply;
    
    event Transfer(address indexed from, address indexed to, uint256 value);
    
    constructor(uint256 initialSupply) {
        totalSupply = initialSupply;
        _balances[msg.sender] = initialSupply;
    }
    
    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }
    
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        require(_balances[msg.sender] >= amount, "Insufficient balance");
        
        _balances[msg.sender] -= amount;
        _balances[to] += amount;
        
        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

// 结合所有权和代币功能的合约
contract ManagedToken is Ownable, BasicToken {
    bool public transfersEnabled;
    
    constructor(uint256 initialSupply) BasicToken(initialSupply) {
        transfersEnabled = true;
    }
    
    // 覆盖父合约的函数
    function transfer(address to, uint256 amount) public override returns (bool) {
        require(transfersEnabled, "Transfers are disabled");
        return super.transfer(to, amount); // 调用父合约的实现
    }
    
    // 只有所有者可以禁用/启用转账
    function setTransfersEnabled(bool enabled) public onlyOwner {
        transfersEnabled = enabled;
    }
    
    // 覆盖并扩展transferOwnership
    function transferOwnership(address newOwner) public override onlyOwner {
        super.transferOwnership(newOwner);
        // 额外逻辑,如记录事件
    }
}

在这个例子中,ManagedToken继承了OwnableBasicToken的功能,并添加了自己的逻辑。override关键字表明函数覆盖了父合约中的函数,而super关键字用于调用父合约的实现。

4. 抽象合约和虚函数

抽象合约包含至少一个未实现的函数(虚函数)。它们不能直接部署,必须被继承并实现所有虚函数。

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

// 抽象合约
abstract contract BaseGame {
    address public owner;
    
    constructor() {
        owner = msg.sender;
    }
    
    // 虚函数 - 没有实现
    function play() public virtual returns (bool);
    
    // 部分实现的函数
    function isOwner() public view returns (bool) {
        return msg.sender == owner;
    }
}

// 实现抽象合约
contract DiceGame is BaseGame {
    uint256 private nonce = 0;
    
    // 实现虚函数
    function play() public override returns (bool) {
        nonce++;
        uint256 diceRoll = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, nonce))) % 6 + 1;
        return diceRoll > 3; // 4, 5, 6为胜利
    }
}

抽象合约和虚函数使你能够定义必须由子合约实现的接口,同时提供一些共享功能。

5. 事件和日志

事件是以太坊虚拟机(EVM)日志机制的抽象。它们允许合约将信息写入区块链日志,这些日志可以被外部应用程序高效地访问。

事件的高级用法

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

contract AdvancedEvents {
    // 定义事件,最多可以有3个indexed参数
    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
    event ComplexEvent(
        address indexed primary,
        address indexed secondary,
        uint256 indexed code,
        string details,
        bytes data
    );
    
    function transfer(address to, uint256 amount) public {
        // 业务逻辑...
        
        // 触发事件
        emit Transfer(msg.sender, to, amount);
    }
    
    function approve(address spender, uint256 amount) public {
        // 业务逻辑...
        
        // 触发事件
        emit Approval(msg.sender, spender, amount);
    }
    
    function complexOperation(
        address secondary,
        uint256 code,
        string memory details,
        bytes memory data
    ) public {
        // 业务逻辑...
        
        // 触发复杂事件
        emit ComplexEvent(msg.sender, secondary, code, details, data);
    }
}

indexed参数作为主题存储,允许高效过滤和搜索。非索引参数存储在日志的数据部分。

6. 汇编和底层操作

Solidity允许使用内联汇编访问以太坊虚拟机的底层功能。这提供了更多的控制,但也增加了复杂性和风险。

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

contract AssemblyExample {
    function addAssembly(uint256 a, uint256 b) public pure returns (uint256 c) {
        assembly {
            // 直接在EVM中执行加法
            c := add(a, b)
            
            // 如果结果小于a,说明发生了溢出
            if lt(c, a) { revert(0, 0) }
        }
    }
    
    function getBlockHash(uint256 blockNumber) public view returns (bytes32 hash) {
        assembly {
            // 使用blockhash操作码
            hash := blockhash(blockNumber)
        }
    }
    
    function callWithExactGas(address target, uint256 gasAmount, bytes memory data) public returns (bool success) {
        assembly {
            // 获取data的长度和指针
            let len := mload(data)
            let ptr := add(data, 0x20)
            
            // 使用指定的gas调用目标合约
            success := call(gasAmount, target, 0, ptr, len, 0, 0)
        }
    }
}

内联汇编应谨慎使用,通常只在需要优化gas或访问Solidity不直接暴露的EVM功能时使用。

智能合约设计模式:构建更好的DApps

设计模式是解决常见问题的可重用解决方案。以下是Solidity中一些流行的设计模式:

1. 代理模式(Proxy Pattern)

代理模式允许合约逻辑升级,同时保持状态和地址不变。

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

// 存储合约(保持不变)
contract Storage {
    address public implementation;
    address public admin;
    mapping(bytes32 => uint256) private values;
    
    constructor() {
        admin = msg.sender;
    }
    
    function setValue(bytes32 key, uint256 value) external {
        require(msg.sender == address(this), "Only implementation can set values");
        values[key] = value;
    }
    
    function getValue(bytes32 key) external view returns (uint256) {
        return values[key];
    }
    
    function setImplementation(address newImplementation) external {
        require(msg.sender == admin, "Only admin can upgrade");
        implementation = newImplementation;
    }
    
    // 将所有调用委托给实现合约
    fallback() external payable {
        address impl = implementation;
        require(impl != address(0), "Implementation not set");
        
        assembly {
            // 复制调用数据
            calldatacopy(0, 0, calldatasize())
            
            // 调用实现合约
            let success := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
            
            // 复制返回数据
            returndatacopy(0, 0, returndatasize())
            
            // 根据调用结果返回或回滚
            switch success
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
}

// 实现合约V1
contract ImplementationV1 {
    function setValue(bytes32 key, uint256 value) external {
        Storage(address(this)).setValue(key, value);
    }
    
    function getValue(bytes32 key) external view returns (uint256) {
        return Storage(address(this)).getValue(key);
    }
    
    function version() external pure returns (string memory) {
        return "V1";
    }
}

// 实现合约V2(升级版)
contract ImplementationV2 {
    function setValue(bytes32 key, uint256 value) external {
        Storage(address(this)).setValue(key, value);
    }
    
    function getValue(bytes32 key) external view returns (uint256) {
        return Storage(address(this)).getValue(key);
    }
    
    function version() external pure returns (string memory) {
        return "V2";
    }
    
    // 新功能
    function doubleValue(bytes32 key) external {
        uint256 value = Storage(address(this)).getValue(key);
        Storage(address(this)).setValue(key, value * 2);
    }
}

使用代理模式,你可以升级合约逻辑而不改变用户交互的地址,也不丢失状态数据。

2. 工厂模式(Factory Pattern)

工厂模式用于创建合约的多个实例,常用于创建标准化但可定制的合约。

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

// 产品合约
contract Vault {
    address public owner;
    address public factory;
    string public name;
    
    constructor(address _owner, string memory _name) {
        owner = _owner;
        factory = msg.sender;
        name = _name;
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }
    
    function deposit() external payable {
        // 存款逻辑
    }
    
    function withdraw(uint256 amount) external onlyOwner {
        require(address(this).balance >= amount, "Insufficient balance");
        payable(owner).transfer(amount);
    }
}

// 工厂合约
contract VaultFactory {
    mapping(address => address[]) public userVaults;
    address[] public allVaults;
    
    event VaultCreated(address indexed owner, address vault, string name);
    
    function createVault(string memory name) external returns (address) {
        Vault newVault = new Vault(msg.sender, name);
        
        userVaults[msg.sender].push(address(newVault));
        allVaults.push(address(newVault));
        
        emit VaultCreated(msg.sender, address(newVault), name);
        return address(newVault);
    }
    
    function getUserVaults(address user) external view returns (address[] memory) {
        return userVaults[user];
    }
    
    function getVaultCount() external view returns (uint256) {
        return allVaults.length;
    }
}

工厂模式使创建多个相似合约变得简单,同时提供了一个中央注册表来跟踪所有实例。

3. 检查-效果-交互模式(Checks-Effects-Interactions Pattern)

这种模式通过按特定顺序组织代码来防止重入攻击:

  1. 检查所有前提条件
  2. 修改合约状态
  3. 与其他合约交互
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SecureBank {
    mapping(address => uint256) private balances;
    
    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }
    
    // 不安全的提款函数
    function unsafeWithdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        
        // 危险:在更新状态前与外部合约交互
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        
        balances[msg.sender] -= amount; // 如果发生重入攻击,这行可能永远不会执行
    }
    
    // 安全的提款函数(检查-效果-交互)
    function safeWithdraw(uint256 amount) external {
        // 1. 检查
        require(balances[msg.sender] >= amount, "Insufficient balance");
        
        // 2. 效果(状态更新)
        balances[msg.sender] -= amount;
        
        // 3. 交互
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
    
    function getBalance() external view returns (uint256) {
        return balances[msg.sender];
    }
}

这种模式是智能合约安全的基础,应该在所有涉及外部调用的函数中使用。

4. 提款模式(Pull Payment Pattern)

提款模式将发送资金的责任从合约转移到接收者,避免了许多与直接发送以太币相关的问题。

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

contract PullPayment {
    mapping(address => uint256) private payments;
    
    event FundsSent(address indexed recipient, uint256 amount);
    event FundsWithdrawn(address indexed recipient, uint256 amount);
    
    // 记录应付款项,但不立即发送
    function sendPayment(address recipient, uint256 amount) public payable {
        require(msg.value == amount, "Must send exact amount");
        payments[recipient] += amount;
        emit FundsSent(recipient, amount);
    }
    
    // 接收者自己提取资金
    function withdrawPayment() public {
        uint256 amount = payments[msg.sender];
        require(amount > 0, "No funds to withdraw");
        
        // 检查-效果-交互模式
        payments[msg.sender] = 0;
        
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        
        emit FundsWithdrawn(msg.sender, amount);
    }
    
    function getPayment(address recipient) public view returns (uint256) {
        return payments[recipient];
    }
}

提款模式避免了发送以太币时可能出现的失败,并将gas成本转移给了接收者。

5. 状态机模式(State Machine Pattern)

状态机模式用于管理合约的不同状态和状态之间的转换。

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

contract Crowdfunding {
    enum State { Fundraising, Successful, Failed, Closed }
    
    State public state;
    address public creator;
    uint256 public goal;
    uint256 public endTime;
    mapping(address => uint256) public contributions;
    uint256 public totalRaised;
    
    modifier inState(State _state) {
        require(state == _state, "Invalid state");
        _;
    }
    
    constructor(uint256 _goal, uint256 durationDays) {
        creator = msg.sender;
        goal = _goal;
        endTime = block.timestamp + (durationDays * 1 days);
        state = State.Fundraising;
    }
    
    function contribute() public payable inState(State.Fundraising) {
        require(block.timestamp < endTime, "Campaign ended");
        contributions[msg.sender] += msg.value;
        totalRaised += msg.value;
        
        if (totalRaised >= goal) {
            state = State.Successful;
        }
    }
    
    function checkStatus() public {
        if (state == State.Fundraising && block.timestamp >= endTime) {
            if (totalRaised >= goal) {
                state = State.Successful;
            } else {
                state = State.Failed;
            }
        }
    }
    
    function claimFunds() public inState(State.Successful) {
        require(msg.sender == creator, "Only creator can claim");
        state = State.Closed;
        payable(creator).transfer(address(this).balance);
    }
    
    function refund() public inState(State.Failed) {
        require(contributions[msg.sender] > 0, "No contribution to refund");
        uint256 amount = contributions[msg.sender];
        contributions[msg.sender] = 0;
        payable(msg.sender).transfer(amount);
    }
}

状态机模式使复杂的业务流程更容易管理,并确保操作只在适当的状态下执行。

实战项目:去中心化市场

让我们将所学的知识应用到一个实际项目中——一个去中心化的二手商品市场。这个项目将展示多种Solidity特性和设计模式。

项目概述

我们将创建一个去中心化市场,允许用户:

  • 列出商品销售
  • 购买其他用户的商品
  • 管理订单状态
  • 提交和查看评价
  • 提取收益

这个项目将使用多种我们学过的设计模式和安全实践,包括:

  • 检查-效果-交互模式
  • 提款模式
  • 访问控制
  • 事件记录
  • 安全转账

合约结构

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

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title DecentralizedMarketplace
 * @dev 一个去中心化的二手商品市场,允许用户列出、购买和评价商品
 */
contract DecentralizedMarketplace is ReentrancyGuard, Ownable {
    // 商品结构
    struct Item {
        uint256 id;
        address seller;
        string title;
        string description;
        uint256 price;
        bool available;
        uint256 createdAt;
    }
    
    // 订单结构
    struct Order {
        uint256 id;
        uint256 itemId;
        address buyer;
        address seller;
        uint256 price;
        OrderStatus status;
        uint256 createdAt;
    }
    
    // 评价结构
    struct Review {
        address reviewer;
        address reviewee;
        uint256 orderId;
        uint8 rating; // 1-5
        string comment;
        uint256 createdAt;
    }
    
    // 订单状态枚举
    enum OrderStatus { Created, Shipped, Received, Cancelled, Disputed, Resolved }
    
    // 状态变量
    uint256 private nextItemId = 1;
    uint256 private nextOrderId = 1;
    uint256 public platformFeePercent = 2; // 2%
    
    mapping(uint256 => Item) public items;
    mapping(uint256 => Order) public orders;
    mapping(address => uint256[]) public userItems;
    mapping(address => uint256[]) public userOrders;
    mapping(address => uint256) public userBalances;
    mapping(uint256 => Review[]) public orderReviews;
    mapping(address => mapping(address => uint256)) public userRatings; // 用户评分总和
    mapping(address => mapping(address => uint256)) public userRatingCounts; // 用户评分计数
    
    // 事件
    event ItemListed(uint256 indexed itemId, address indexed seller, string title, uint256 price);
    event ItemUpdated(uint256 indexed itemId, string title, uint256 price, bool available);
    event ItemPurchased(uint256 indexed orderId, uint256 indexed itemId, address indexed buyer, uint256 price);
    event OrderStatusChanged(uint256 indexed orderId, OrderStatus status);
    event ReviewSubmitted(uint256 indexed orderId, address indexed reviewer, address indexed reviewee, uint8 rating);
    event FundsWithdrawn(address indexed user, uint256 amount);
    
    // 修饰符
    modifier onlyItemOwner(uint256 itemId) {
        require(items[itemId].seller == msg.sender, "Not the item owner");
        _;
    }
    
    modifier itemExists(uint256 itemId) {
        require(itemId > 0 && itemId < nextItemId, "Item does not exist");
        _;
    }
    
    modifier orderExists(uint256 orderId) {
        require(orderId > 0 && orderId < nextOrderId, "Order does not exist");
        _;
    }
    
    modifier onlyOrderParticipant(uint256 orderId) {
        require(
            orders[orderId].buyer == msg.sender || orders[orderId].seller == msg.sender,
            "Not a participant in this order"
        );
        _;
    }

这是我们市场合约的基本结构,包括数据结构、状态变量、事件和修饰符。接下来,我们将实现核心功能。

商品管理功能

    /**
     * @dev 列出新商品
     */
    function listItem(string memory title, string memory description, uint256 price) external returns (uint256) {
        require(bytes(title).length > 0, "Title cannot be empty");
        require(price > 0, "Price must be greater than zero");
        
        uint256 itemId = nextItemId++;
        
        items[itemId] = Item({
            id: itemId,
            seller: msg.sender,
            title: title,
            description: description,
            price: price,
            available: true,
            createdAt: block.timestamp
        });
        
        userItems[msg.sender].push(itemId);
        
        emit ItemListed(itemId, msg.sender, title, price);
        return itemId;
    }
    
    /**
     * @dev 更新商品信息
     */
    function updateItem(uint256 itemId, string memory title, string memory description, uint256 price, bool available) 
        external 
        itemExists(itemId) 
        onlyItemOwner(itemId) 
    {
        require(bytes(title).length > 0, "Title cannot be empty");
        require(price > 0, "Price must be greater than zero");
        
        Item storage item = items[itemId];
        item.title = title;
        item.description = description;
        item.price = price;
        item.available = available;
        
        emit ItemUpdated(itemId, title, price, available);
    }
    
    /**
     * @dev 获取商品详情
     */
    function getItem(uint256 itemId) external view itemExists(itemId) returns (
        uint256 id,
        address seller,
        string memory title,
        string memory description,
        uint256 price,
        bool available,
        uint256 createdAt
    ) {
        Item storage item = items[itemId];
        return (
            item.id,
            item.seller,
            item.title,
            item.description,
            item.price,
            item.available,
            item.createdAt
        );
    }
    
    /**
     * @dev 获取用户的所有商品
     */
    function getUserItems(address user) external view returns (uint256[] memory) {
        return userItems[user];
    }

订单和购买功能

    /**
     * @dev 购买商品
     */
    function purchaseItem(uint256 itemId) external payable nonReentrant itemExists(itemId) {
        Item storage item = items[itemId];
        
        require(item.available, "Item is not available");
        require(item.seller != msg.sender, "Cannot buy your own item");
        require(msg.value >= item.price, "Insufficient payment");
        
        // 创建订单
        uint256 orderId = nextOrderId++;
        orders[orderId] = Order({
            id: orderId,
            itemId: itemId,
            buyer: msg.sender,
            seller: item.seller,
            price: item.price,
            status: OrderStatus.Created,
            createdAt: block.timestamp
        });
        
        // 更新商品状态
        item.available = false;
        
        // 记录订单
        userOrders[msg.sender].push(orderId);
        userOrders[item.seller].push(orderId);
        
        // 计算平台费用
        uint256 platformFee = (item.price * platformFeePercent) / 100;
        uint256 sellerAmount = item.price - platformFee;
        
        // 更新卖家余额(使用提款模式)
        userBalances[item.seller] += sellerAmount;
        
        // 退还多余的以太币
        if (msg.value > item.price) {
            payable(msg.sender).transfer(msg.value - item.price);
        }
        
        emit ItemPurchased(orderId, itemId, msg.sender, item.price);
    }
    
    /**
     * @dev 更新订单状态
     */
    function updateOrderStatus(uint256 orderId, OrderStatus newStatus) 
        external 
        orderExists(orderId) 
        onlyOrderParticipant(orderId) 
    {
        Order storage order = orders[orderId];
        
        // 验证状态转换的有效性
        if (newStatus == OrderStatus.Shipped) {
            require(msg.sender == order.seller, "Only seller can mark as shipped");
            require(order.status == OrderStatus.Created, "Invalid status transition");
        } else if (newStatus == OrderStatus.Received) {
            require(msg.sender == order.buyer, "Only buyer can mark as received");
            require(order.status == OrderStatus.Shipped, "Item must be shipped first");
        } else if (newStatus == OrderStatus.Disputed) {
            require(order.status != OrderStatus.Disputed, "Already disputed");
            require(order.status != OrderStatus.Resolved, "Already resolved");
        } else if (newStatus == OrderStatus.Cancelled) {
            require(
                order.status == OrderStatus.Created || 
                (order.status == OrderStatus.Shipped && msg.sender == order.seller),
                "Cannot cancel at this stage"
            );
            
            // 如果卖家取消,退还买家资金
            if (msg.sender == order.seller) {
                userBalances[order.buyer] += order.price;
                userBalances[order.seller] -= order.price;
            }
        }
        
        order.status = newStatus;
        emit OrderStatusChanged(orderId, newStatus);
    }
    
    /**
     * @dev 获取订单详情
     */
    function getOrder(uint256 orderId) external view orderExists(orderId) returns (
        uint256 id,
        uint256 itemId,
        address buyer,
        address seller,
        uint256 price,
        OrderStatus status,
        uint256 createdAt
    ) {
        Order storage order = orders[orderId];
        return (
            order.id,
            order.itemId,
            order.buyer,
            order.seller,
            order.price,
            order.status,
            order.createdAt
        );
    }
    
    /**
     * @dev 获取用户的所有订单
     */
    function getUserOrders(address user) external view returns (uint256[] memory) {
        return userOrders[user];
    }

这些函数实现了商品列表和订单管理的核心功能。接下来,我们将添加评价系统和资金管理功能。

评价系统

    /**
     * @dev 提交评价
     */
    function submitReview(uint256 orderId, address reviewee, uint8 rating, string memory comment) 
        external 
        orderExists(orderId) 
        onlyOrderParticipant(orderId) 
    {
        Order storage order = orders[orderId];
        
        // 验证评价参数
        require(rating >= 1 && rating <= 5, "Rating must be between 1 and 5");
        require(
            (msg.sender == order.buyer && reviewee == order.seller) || 
            (msg.sender == order.seller && reviewee == order.buyer),
            "Invalid reviewer or reviewee"
        );
        require(order.status == OrderStatus.Received || order.status == OrderStatus.Resolved, 
            "Order must be completed before review"
        );
        
        // 创建评价
        Review memory review = Review({
            reviewer: msg.sender,
            reviewee: reviewee,
            orderId: orderId,
            rating: rating,
            comment: comment,
            createdAt: block.timestamp
        });
        
        // 存储评价
        orderReviews[orderId].push(review);
        
        // 更新用户评分
        userRatings[reviewee][msg.sender] = rating;
        userRatingCounts[reviewee][msg.sender] = 1;
        
        emit ReviewSubmitted(orderId, msg.sender, reviewee, rating);
    }
    
    /**
     * @dev 获取订单的所有评价
     */
    function getOrderReviews(uint256 orderId) external view orderExists(orderId) returns (Review[] memory) {
        return orderReviews[orderId];
    }
    
    /**
     * @dev 计算用户的平均评分
     */
    function getUserAverageRating(address user) external view returns (uint256) {
        uint256 totalRating = 0;
        uint256 count = 0;
        
        for (uint256 i = 0; i < userOrders[user].length; i++) {
            uint256 orderId = userOrders[user][i];
            Order storage order = orders[orderId];
            
            address counterparty = (user == order.buyer) ? order.seller : order.buyer;
            if (userRatingCounts[user][counterparty] > 0) {
                totalRating += userRatings[user][counterparty];
                count += userRatingCounts[user][counterparty];
            }
        }
        
        if (count == 0) return 0;
        return totalRating / count;
    }

资金管理功能

    /**
     * @dev 提取余额
     */
    function withdrawFunds() external nonReentrant {
        uint256 amount = userBalances[msg.sender];
        require(amount > 0, "No funds to withdraw");
        
        // 检查-效果-交互模式
        userBalances[msg.sender] = 0;
        
        (bool success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "Transfer failed");
        
        emit FundsWithdrawn(msg.sender, amount);
    }
    
    /**
     * @dev 获取用户余额
     */
    function getBalance() external view returns (uint256) {
        return userBalances[msg.sender];
    }
    
    /**
     * @dev 平台提取费用
     */
    function withdrawPlatformFees() external onlyOwner nonReentrant {
        uint256 platformBalance = address(this).balance;
        for (uint256 i = 1; i < nextOrderId; i++) {
            platformBalance -= userBalances[orders[i].seller];
        }
        
        require(platformBalance > 0, "No platform fees to withdraw");
        
        (bool success, ) = payable(owner()).call{value: platformBalance}("");
        require(success, "Transfer failed");
    }
    
    /**
     * @dev 更新平台费率
     */
    function updatePlatformFee(uint256 newFeePercent) external onlyOwner {
        require(newFeePercent <= 10, "Fee too high"); // 最高10%
        platformFeePercent = newFeePercent;
    }
    
    /**
     * @dev 解决争议
     */
    function resolveDispute(uint256 orderId, address winner) 
        external 
        onlyOwner 
        orderExists(orderId) 
    {
        Order storage order = orders[orderId];
        require(order.status == OrderStatus.Disputed, "Order not disputed");
        require(
            winner == order.buyer || winner == order.seller,
            "Winner must be buyer or seller"
        );
        
        // 如果买家胜诉,退还资金
        if (winner == order.buyer) {
            userBalances[order.buyer] += order.price;
            userBalances[order.seller] -= order.price;
        }
        
        order.status = OrderStatus.Resolved;
        emit OrderStatusChanged(orderId, OrderStatus.Resolved);
    }
}

这些函数实现了评价系统和资金管理的功能,包括提交评价、计算用户评分、提取资金和解决争议等。

项目总结

我们的去中心化市场合约现在已经完成,它包含了以下功能:

  1. 商品管理:用户可以列出、更新和查询商品
  2. 订单处理:用户可以购买商品并管理订单状态
  3. 评价系统:用户可以对交易对手进行评价
  4. 资金管理:安全的资金处理和提款机制
  5. 争议解决:平台可以介入解决买卖双方的争议

这个项目展示了多种Solidity高级特性和设计模式:

  • 继承:从OpenZeppelin合约继承安全功能
  • 修饰符:用于访问控制和验证
  • 枚举:用于订单状态管理
  • 结构体:组织复杂数据
  • 事件:记录重要操作
  • 检查-效果-交互模式:防止重入攻击
  • 提款模式:安全的资金处理
  • 状态机模式:管理订单状态转换

前端集成

要将这个智能合约与前端应用程序集成,你可以使用Web3.js或ethers.js库。以下是使用ethers.js的简单示例:

// 连接到合约
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const marketplaceContract = new ethers.Contract(contractAddress, contractABI, signer);

// 列出商品
async function listItem(title, description, price) {
  try {
    const tx = await marketplaceContract.listItem(title, description, ethers.utils.parseEther(price));
    await tx.wait();
    console.log("Item listed successfully!");
  } catch (error) {
    console.error("Error listing item:", error);
  }
}

// 购买商品
async function purchaseItem(itemId, price) {
  try {
    const tx = await marketplaceContract.purchaseItem(itemId, {
      value: ethers.utils.parseEther(price)
    });
    await tx.wait();
    console.log("Item purchased successfully!");
  } catch (error) {
    console.error("Error purchasing item:", error);
  }
}

// 监听事件
marketplaceContract.on("ItemListed", (itemId, seller, title, price, event) => {
  console.log(`New item listed: ${title} by ${seller} for ${ethers.utils.formatEther(price)} ETH`);
});

网站公告

今日签到

点亮在社区的每一天
去签到