solidity从入门到精通 第七章:高级特性与实战项目
第七章:高级特性与实战项目
从学徒到大师:高级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;}// 覆盖并扩展transferOwnershipfunction 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 successcase 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-5string 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`);
});