Web3新速度:Monad与BuyEarthDApp重塑虚拟世界Web3时代,速度决定未来!Monad作为一款高性能的以太坊兼容L1区块链,以每秒10,000+的交易处理速度(TPS)突破传统区块链瓶颈,为去中心化应用(DApp)开辟了新天地。BuyEarthDApp是这一技术的生动实践,让用
Web3时代,速度决定未来!Monad作为一款高性能的以太坊兼容L1区块链,以每秒10,000+的交易处理速度(TPS)突破传统区块链瓶颈,为去中心化应用(DApp)开辟了新天地。BuyEarth DApp是这一技术的生动实践,让用户在虚拟世界中购买土地、定制颜色,体验区块链驱动的数字经济。本文将带你走进Monad的超速世界,探索BuyEarth如何重塑Web3虚拟市场,揭秘高性能区块链与DApp的完美融合!
Monad是一款以太坊兼容的L1区块链,通过并行执行和乐观执行实现10,000+ TPS,远超以太坊的22.7 TPS,为Web3 DApp提供高性能基础设施。BuyEarth DApp基于Monad打造,用户可支付0.001 ETH购买虚拟土地并设置颜色,合约拥有者可管理资金与状态。项目采用Foundry开发,测试覆盖率达100%(行)与92.86%(分支),前端基于Next.js并部署于Vercel。本文详解Monad的技术优势、BuyEarth的功能与开发流程,展现Web3 DApp如何在超速区块链上重塑虚拟世界。
Monad is a high-performance Ethereum-compatible Layer 1 blockchain, redefining the balance between decentralization and scalability for Web3 applications.
Monad 是一个高性能的以太坊兼容L1区块链,为Web3应用重新定义了去中心化与可扩展性的平衡。
想象一个区块链每秒处理10,000+笔交易,轻松碾压以太坊的22.7 TPS,却依然与以太坊生态无缝兼容——这就是 Monad!它通过并行执行技术,打破传统 EVM 链逐一处理交易的限制,让 BuyEarth 这样的去中心化应用(DApp)实现秒级响应,支撑虚拟世界中的土地交易热潮。开发者无需改动代码,就能将以太坊应用迁移到 Monad,享受超速体验。
Monad 的秘诀在于并行执行和乐观执行:交易无需排队等待,数据跟踪和重试机制确保结果一致。搭配状态无关计算(减少存储负担)、数据缓存(加速访问)和静态代码分析(智能调度),Monad 为 BuyEarth 的土地购买和颜色定制提供流畅支持。未来,Monad 将引入 AI 预测交易依赖,最大化效率,点燃 Web3 DApp 的无限可能!
Monad 的超速性能如何赋能 BuyEarth DApp?接下来,让我们进入智能合约实操,揭秘虚拟土地交易的实现细节!
mcd buyearth # mkdir buyearth && cd buyearth
forge init --template https://github.com/qiaopengjun5162/foundry-template
qiaopengjun in buyearth on main
❯ tree -L 3 -I "node_modules|.DS_Store|out|buy-earth-dapp|lib|dapp-ui|broadcast|cache"
.
├── _typos.toml
├── buy-earth-dapp.zip
├── CHANGELOG.md
├── cliff.toml
├── depoly.md
├── foundry.toml
├── LICENSE
├── README.md
├── remappings.txt
├── script
│ ├── BuyEarth.s.sol
│ └── deploy.sh
├── slither.config.json
├── src
│ ├── BuyEarth.sol
│ └── utils
│ └── EmptyContract.sol
├── style_guide.md
└── test
└── BuyEarth.t.sol
5 directories, 16 files
BuyEarth.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract BuyEarth {
uint256 private constant PRICE = 0.001 ether;
address private owner;
uint256[100] private squares;
address[] private depositorList;
mapping(address => uint256) public userDeposits;
event BuySquare(uint8 idx, uint256 color);
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
event ColorChanged(uint8 indexed idx, uint256 color);
event Deposited(address indexed sender, uint256 amount);
event Receive(address indexed sender, uint256 amount);
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
constructor() {
owner = msg.sender;
}
function getSquares() public view returns (uint256[] memory) {
uint256[] memory _squares = new uint256[](100);
for (uint256 i = 0; i < 100; i++) {
_squares[i] = squares[i];
}
return _squares;
}
function buySquare(uint8 idx, uint256 color) public payable {
// === Checks ===
require(idx < 100, "Invalid square number");
require(msg.value >= PRICE, "Incorrect price");
require(color <= 0xFFFFFF, "Invalid color");
uint256 change = msg.value - PRICE;
// === Effects ===
squares[idx] = color;
emit BuySquare(idx, color);
// === Interactions ===
if (change > 0) {
(bool success1,) = msg.sender.call{value: change}("");
require(success1, "Change return failed");
}
(bool success2,) = owner.call{value: PRICE}("");
require(success2, "Owner payment failed");
}
function deposit() public payable {
require(msg.value > 0, "Must send some ETH");
// 如果是新存款用户,添加到列表
if (userDeposits[msg.sender] == 0) {
depositorList.push(msg.sender);
}
userDeposits[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
function withdrawTo(address recipient) public onlyOwner {
require(recipient != address(0), "Invalid recipient address");
uint256 balance = address(this).balance;
require(balance > 0, "No funds to withdraw");
require(depositorList.length > 0, "No depositors");
address[] memory depositors = _getAllDepositors(); // 获取所有存款用户
for (uint256 i = 0; i < depositors.length; i++) {
userDeposits[depositors[i]] = 0;
}
(bool success,) = recipient.call{value: balance}("");
require(success, "Withdrawal failed");
}
function setOwner(address newOwner) public onlyOwner {
require(newOwner != address(0), "Invalid owner address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
function getOwner() public view returns (address) {
return owner;
}
function getColor(uint8 idx) public view returns (uint256) {
return squares[idx];
}
function setColor(uint8 idx, uint256 color) public onlyOwner {
require(idx < 100, "Invalid square number");
squares[idx] = color;
emit ColorChanged(idx, color);
}
function getUserDeposits(address user) public view returns (uint256) {
return userDeposits[user];
}
function _getAllDepositors() private view returns (address[] memory) {
return depositorList;
}
receive() external payable {
emit Receive(msg.sender, msg.value);
deposit();
}
}
智能合约 BuyEarth 说明
以下是对 BuyEarth 智能合约的详细说明,旨在帮助读者理解其功能、结构和用途。合约基于 Solidity 语言编写,部署在以太坊区块链上,允许用户购买虚拟“地块”、设置颜色、存款、提取资金,并由合约拥有者管理关键操作。
BuyEarth 是一个去中心化应用(DApp)的智能合约,模拟一个虚拟土地市场。用户可以:
合约使用事件(event)记录关键操作,便于前端应用监听和展示。安全机制(如权限检查、输入验证)确保合约的可靠性和安全性。
许可和编译器版本
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
状态变量
uint256 private constant PRICE = 0.001 ether;
address private owner;
uint[100] private squares;
address[] private depositorList;
mapping(address => uint256) public userDeposits;
事件
event BuySquare(uint8 idx, uint color);
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
event ColorChanged(uint8 indexed idx, uint color);
event Deposited(address indexed sender, uint256 amount);
event Receive(address indexed sender, uint256 amount);
修饰符
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
构造函数
constructor() {
owner = msg.sender;
}
查询地块信息
function getSquares() public view returns (uint[] memory) {
uint[] memory _squares = new uint[](100);
for (uint i = 0; i < 100; i++) {
_squares[i] = squares[i];
}
return _squares;
}
function getColor(uint8 idx) public view returns (uint) {
return squares[idx];
}
购买地块
function buySquare(uint8 idx, uint color) public payable {
require(idx < 100, "Invalid square number");
require(msg.value >= PRICE, "Incorrect price");
require(color <= 0xFFFFFF, "Invalid color");
uint256 change = msg.value - PRICE;
squares[idx] = color;
emit BuySquare(idx, color);
if (change > 0) {
(bool success1, ) = msg.sender.call{value: change}("");
require(success1, "Change return failed");
}
(bool success2, ) = owner.call{value: PRICE}("");
require(success2, "Owner payment failed");
}
存款
function deposit() public payable {
require(msg.value > 0, "Must send some ETH");
if (userDeposits[msg.sender] == 0) {
depositorList.push(msg.sender);
}
userDeposits[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}
提取资金
function withdrawTo(address recipient) public onlyOwner {
require(recipient != address(0), "Invalid recipient address");
uint256 balance = address(this).balance;
require(balance > 0, "No funds to withdraw");
address[] memory depositors = _getAllDepositors();
for (uint i = 0; i < depositors.length; i++) {
userDeposits[depositors[i]] = 0;
}
(bool success, ) = recipient.call{value: balance}("");
require(success, "Withdrawal failed");
}
所有权管理
function setOwner(address newOwner) public onlyOwner {
require(newOwner != address(0), "Invalid owner address");
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
function getOwner() public view returns (address) {
return owner;
}
更改地块颜色
function setColor(uint8 idx, uint color) public onlyOwner {
require(idx < 100, "Invalid square number");
squares[idx] = color;
emit ColorChanged(idx, color);
}
查询用户存款
function getUserDeposits(address user) public view returns (uint256) {
return userDeposits[user];
}
接收以太币
receive() external payable {
emit Receive(msg.sender, msg.value);
deposit();
}
辅助函数
function _getAllDepositors() private view returns (address[] memory) {
return depositorList;
}
模块化设计
安全性
可扩展性
BuyEarth 是一个功能完整、安全性较高的智能合约,适合用于虚拟土地购买、颜色设置和资金管理的去中心化应用。其清晰的代码结构、事件机制和安全检查使其易于集成到前端应用中。通过合理的扩展和优化,合约可支持更复杂的业务逻辑和用户交互场景。
BuyEarth.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Test, console} from "forge-std/Test.sol";
import {BuyEarth} from "../src/BuyEarth.sol";
import {BuyEarthScript} from "../script/BuyEarth.s.sol";
contract BuyEarthTest is Test {
BuyEarth public buyEarth;
Account public owner = makeAccount("owner");
address public user = makeAddr("user"); // 测试用户地址
address public user2 = makeAddr("user2"); // 第二个测试用户地址
address public receipt = makeAddr("receipt"); // 收款地址
uint256 private PRICE = 0.001 ether;
event BuySquare(uint8 idx, uint256 color);
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
event ColorChanged(uint8 indexed idx, uint256 color);
event Deposited(address indexed sender, uint256 amount);
function setUp() public {
// 初始化合约
vm.startPrank(owner.addr);
buyEarth = new BuyEarth();
vm.stopPrank();
}
// 测试初始状态
function testInitialState() public view {
assertEq(buyEarth.getOwner(), owner.addr);
uint256[] memory squares = buyEarth.getSquares();
assertEq(squares.length, 100);
for (uint256 i = 0; i < 100; i++) {
assertEq(squares[i], 0);
}
}
// 测试购买格子
function testBuySquare() public {
uint8 testIdx = 1;
uint256 testColor = 0xFF0000; // 红色
uint256 price = PRICE;
// 模拟用户操作
vm.startPrank(user);
deal(user, price * 2); // 分配双倍资金用于测试找零
buyEarth.buySquare{value: price * 2}(testIdx, testColor);
vm.stopPrank();
// 验证格子颜色被修改
assertEq(buyEarth.getColor(testIdx), testColor);
assertEq(user.balance, price); // 应退回price的找零
}
// 测试购买格子边界条件
function testBuySquareEdgeCases() public {
uint256 price = PRICE;
deal(user, price);
// 测试无效的格子索引
vm.startPrank(user);
vm.expectRevert("Invalid square number");
buyEarth.buySquare{value: price}(100, 0xFF0000);
vm.stopPrank();
// 测试无效的颜色值
vm.startPrank(user);
vm.expectRevert("Invalid color");
buyEarth.buySquare{value: price}(1, 0x1000000);
vm.stopPrank();
// 测试金额不足
vm.startPrank(user);
vm.expectRevert("Incorrect price");
buyEarth.buySquare{value: price - 1}(1, 0xFF0000);
vm.stopPrank();
}
// 测试多个用户购买不同格子
function testMultipleUsersBuySquares() public {
uint256 price = PRICE;
deal(user, price);
deal(user2, price);
vm.startPrank(user);
buyEarth.buySquare{value: price}(1, 0xFF0000);
vm.stopPrank();
vm.startPrank(user2);
buyEarth.buySquare{value: price}(2, 0x00FF00);
vm.stopPrank();
assertEq(buyEarth.getColor(1), 0xFF0000);
assertEq(buyEarth.getColor(2), 0x00FF00);
}
// 测试所有者功能
function testOwnerFunctions() public {
// 测试设置颜色
vm.startPrank(owner.addr);
buyEarth.setColor(1, 0x0000FF);
assertEq(buyEarth.getColor(1), 0x0000FF);
vm.stopPrank();
// 测试非所有者不能设置颜色
vm.startPrank(user);
vm.expectRevert("Only owner can call this function");
buyEarth.setColor(1, 0x0000FF);
vm.stopPrank();
// 测试转移所有权
vm.startPrank(owner.addr);
buyEarth.setOwner(user);
assertEq(buyEarth.getOwner(), user);
vm.stopPrank();
}
// 测试事件
function testEvents() public {
uint8 testIdx = 1;
uint256 testColor = 0xFF0000;
uint256 price = PRICE;
deal(user, price);
// 测试购买格子事件
vm.startPrank(user);
vm.expectEmit(true, true, true, true);
emit BuySquare(testIdx, testColor);
buyEarth.buySquare{value: price}(testIdx, testColor);
vm.stopPrank();
// 测试所有权转移事件
vm.startPrank(owner.addr);
vm.expectEmit(true, true, true, true);
emit OwnershipTransferred(owner.addr, user);
buyEarth.setOwner(user);
vm.stopPrank();
// 测试颜色变更事件
vm.startPrank(user);
vm.expectEmit(true, true, true, true);
emit ColorChanged(testIdx, 0x00FF00);
buyEarth.setColor(testIdx, 0x00FF00);
vm.stopPrank();
}
// 测试直接转账
function testDirectTransfer() public {
deal(user, 1 ether);
vm.startPrank(user);
(bool success,) = address(buyEarth).call{value: 0.1 ether}("");
assertTrue(success); // 验证转账成功
assertEq(user.balance, 0.9 ether);
assertEq(address(buyEarth).balance, 0.1 ether);
vm.stopPrank();
}
// 测试提现边界条件
function testWithdrawToEdgeCases() public {
// 测试零地址提现
vm.startPrank(owner.addr);
vm.expectRevert("Invalid recipient address");
buyEarth.withdrawTo(address(0));
vm.stopPrank();
// 测试无余额提现
vm.startPrank(owner.addr);
vm.expectRevert("No funds to withdraw");
buyEarth.withdrawTo(receipt);
vm.stopPrank();
}
// 测试提现
function testWithdraw() public {
deal(address(buyEarth), 1 ether);
vm.startPrank(owner.addr);
vm.expectRevert("No depositors");
buyEarth.withdrawTo(receipt);
vm.stopPrank();
vm.startPrank(user);
deal(user, 1 ether);
buyEarth.deposit{value: 1 ether}();
vm.stopPrank();
vm.startPrank(owner.addr);
buyEarth.withdrawTo(receipt);
assertEq(address(buyEarth).balance, 0);
assertEq(receipt.balance, 2 ether);
vm.stopPrank();
}
function testDeposit() public {
deal(user, 1 ether);
vm.startPrank(user);
// 明确指定调用合约的deposit()
(bool success,) = address(buyEarth).call{value: 0.1 ether}(abi.encodeWithSignature("deposit()"));
require(success, "Deposit failed");
vm.stopPrank();
assertEq(buyEarth.getUserDeposits(user), 0.1 ether);
}
function testWithdrawFromDeposit() public {
deal(user, 1 ether);
vm.startPrank(user);
// 明确指定调用合约的deposit()
(bool success,) = address(buyEarth).call{value: 0.1 ether}(abi.encodeWithSignature("deposit()"));
require(success, "Deposit failed");
assertEq(buyEarth.getUserDeposits(user), 0.1 ether);
assertEq(user.balance, 0.9 ether);
assertEq(address(buyEarth).balance, 0.1 ether);
vm.stopPrank();
vm.startPrank(owner.addr);
buyEarth.withdrawTo(receipt);
assertEq(address(buyEarth).balance, 0);
assertEq(receipt.balance, 0.1 ether);
vm.stopPrank();
assertEq(buyEarth.getUserDeposits(user), 0);
}
// 新增测试用例 1:测试购买地块时支付刚好等于 PRICE(无退款)
function testBuySquareExactPrice() public {
uint8 testIdx = 3;
uint256 testColor = 0x00FF00; // 绿色
vm.startPrank(user);
deal(user, PRICE);
buyEarth.buySquare{value: PRICE}(testIdx, testColor);
vm.stopPrank();
assertEq(buyEarth.getColor(testIdx), testColor);
assertEq(user.balance, 0); // 无退款,余额应为 0
assertEq(address(buyEarth).balance, 0); // 资金转给 owner
assertEq(owner.addr.balance, PRICE); // owner 收到 PRICE
}
// 新增测试用例 2:测试同一用户多次存款
function testMultipleDeposits() public {
deal(user, 1 ether);
vm.startPrank(user);
// 第一次存款
buyEarth.deposit{value: 0.1 ether}();
assertEq(buyEarth.getUserDeposits(user), 0.1 ether);
// 第二次存款
buyEarth.deposit{value: 0.2 ether}();
assertEq(buyEarth.getUserDeposits(user), 0.3 ether); // 累计存款
vm.stopPrank();
// 验证 depositorList 只记录一次用户地址
// 由于 _getAllDepositors 是 private,间接验证 depositorList 行为
assertEq(address(buyEarth).balance, 0.3 ether);
}
// 新增测试用例 3:测试多用户存款后 withdrawTo 清空 userDeposits
function testWithdrawWithMultipleDepositors() public {
deal(user, 1 ether);
deal(user2, 1 ether);
// 用户 1 存款
vm.startPrank(user);
buyEarth.deposit{value: 0.1 ether}();
vm.stopPrank();
// 用户 2 存款
vm.startPrank(user2);
buyEarth.deposit{value: 0.2 ether}();
vm.stopPrank();
// 验证初始存款
assertEq(buyEarth.getUserDeposits(user), 0.1 ether);
assertEq(buyEarth.getUserDeposits(user2), 0.2 ether);
assertEq(address(buyEarth).balance, 0.3 ether);
// 拥有者提现
vm.startPrank(owner.addr);
buyEarth.withdrawTo(receipt);
vm.stopPrank();
// 验证存款被清空
assertEq(buyEarth.getUserDeposits(user), 0);
assertEq(buyEarth.getUserDeposits(user2), 0);
assertEq(address(buyEarth).balance, 0);
assertEq(receipt.balance, 0.3 ether);
}
// 测试 setColor 的无效索引
function testSetColorInvalidIndex() public {
vm.startPrank(owner.addr);
vm.expectRevert("Invalid square number");
buyEarth.setColor(100, 0xFF0000);
vm.stopPrank();
}
// 测试 receive 函数的零金额转账
function testReceiveZeroAmount() public {
vm.startPrank(user);
vm.expectRevert("Must send some ETH");
(bool success,) = address(buyEarth).call{value: 0}("");
assertTrue(success);
vm.stopPrank();
assertEq(buyEarth.getUserDeposits(user), 0);
assertEq(address(buyEarth).balance, 0);
}
// 测试 owner 支付失败场景(模拟 owner 地址无法接收 ETH)
function testBuySquareOwnerPaymentFailure() public {
// 创建一个无法接收 ETH 的合约作为 owner
NoReceiveETH noReceive = new NoReceiveETH();
vm.startPrank(owner.addr);
buyEarth.setOwner(address(noReceive));
vm.stopPrank();
// 用户尝试购买地块
vm.startPrank(user);
deal(user, PRICE);
vm.expectRevert("Owner payment failed");
buyEarth.buySquare{value: PRICE}(1, 0xFF0000);
vm.stopPrank();
// 验证地块未被更新
assertEq(buyEarth.getColor(1), 0);
}
// 测试 buySquare 中退款失败场景
function testBuySquareChangeReturnFailure() public {
// 创建一个无法接收 ETH 的用户合约
NoReceiveETH noReceiveUser = new NoReceiveETH();
address noReceiveAddr = address(noReceiveUser);
vm.startPrank(noReceiveAddr);
deal(noReceiveAddr, PRICE * 2);
vm.expectRevert("Change return failed");
buyEarth.buySquare{value: PRICE * 2}(1, 0xFF0000);
vm.stopPrank();
// 验证地块未被更新
assertEq(buyEarth.getColor(1), 0);
}
// 测试 withdrawTo 中支付失败场景
function testWithdrawToPaymentFailure() public {
// 创建一个无法接收 ETH 的接收者合约
NoReceiveETH noReceiveRecipient = new NoReceiveETH();
address noReceiveAddr = address(noReceiveRecipient);
// 向合约注入资金
deal(address(buyEarth), 1 ether);
vm.startPrank(owner.addr);
vm.expectRevert("No depositors");
buyEarth.withdrawTo(noReceiveAddr);
vm.stopPrank();
// 验证合约余额未改变
assertEq(address(buyEarth).balance, 1 ether);
}
// 测试 setOwner 的零地址场景
function testSetOwnerZeroAddress() public {
vm.startPrank(owner.addr);
vm.expectRevert("Invalid owner address");
buyEarth.setOwner(address(0));
vm.stopPrank();
// 验证所有者未改变
assertEq(buyEarth.getOwner(), owner.addr);
}
function testReceiveNonZeroAmount() public {
vm.startPrank(user);
deal(user, 0.2 ether);
// 第一次转账:新用户存款
(bool success1,) = address(buyEarth).call{value: 0.1 ether}("");
assertTrue(success1);
assertEq(buyEarth.getUserDeposits(user), 0.1 ether);
// 第二次转账:已有用户存款
(bool success2,) = address(buyEarth).call{value: 0.1 ether}("");
assertTrue(success2);
assertEq(buyEarth.getUserDeposits(user), 0.2 ether);
vm.stopPrank();
}
function testWithdrawWithEmptyDepositorList() public {
// 确保合约有余额但 depositorList 为空
deal(address(buyEarth), 0.1 ether);
vm.startPrank(owner.addr);
vm.expectRevert("No depositors");
buyEarth.withdrawTo(receipt);
vm.stopPrank();
vm.startPrank(user);
deal(user, 0.1 ether);
buyEarth.deposit{value: 0.1 ether}();
vm.stopPrank();
vm.startPrank(owner.addr);
buyEarth.withdrawTo(receipt);
vm.stopPrank();
// 验证提现成功
assertEq(address(buyEarth).balance, 0);
assertEq(receipt.balance, 0.2 ether);
}
function testDeploymentScript() public {
// 设置 PRIVATE_KEY 环境变量
vm.setEnv("PRIVATE_KEY", "0x1"); // 测试用私钥
// 模拟运行部署脚本
BuyEarthScript script = new BuyEarthScript();
BuyEarth deployedContract = script.run();
// 验证初始状态
assertEq(deployedContract.getOwner(), vm.addr(0x1)); // 私钥 0x1 对应的地址
uint256[] memory squares = deployedContract.getSquares();
assertEq(squares.length, 100);
for (uint256 i = 0; i < 100; i++) {
assertEq(squares[i], 0);
}
}
function testOnlyOwnerRestriction() public {
vm.startPrank(user); // 非所有者
vm.expectRevert("Only owner can call this function");
buyEarth.setColor(0,...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!