第七章:高级特性与实战项目
从学徒到大师:高级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
继承了Ownable
和BasicToken
的功能,并添加了自己的逻辑。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)
这种模式通过按特定顺序组织代码来防止重入攻击:
- 检查所有前提条件
- 修改合约状态
- 与其他合约交互
// 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);
}
}
这些函数实现了评价系统和资金管理的功能,包括提交评价、计算用户评分、提取资金和解决争议等。
项目总结
我们的去中心化市场合约现在已经完成,它包含了以下功能:
- 商品管理:用户可以列出、更新和查询商品
- 订单处理:用户可以购买商品并管理订单状态
- 评价系统:用户可以对交易对手进行评价
- 资金管理:安全的资金处理和提款机制
- 争议解决:平台可以介入解决买卖双方的争议
这个项目展示了多种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`);
});