基于 Monad 高性能 EVM 链的密封竞价拍卖应用。买家在 5 轮中先提交价格哈希、再揭露原值,全程链上、无可作弊、无中心化数据库。黑金风格的 Next.js 前端,所有交互即交易。
传统拍卖中出价公开、易被狙击("狙击"指看到别人出价后临时加价)。本项目的做法是 Commit-Reveal(提交-揭露):
- 提交阶段:买家只把
keccak256(price, bidder, salt)这个哈希上链,价格本身不公开展示; - 揭露阶段:买家再上链公布
(price, salt),合约用同样的哈希函数验证一致性; - 任何人都无法在提交阶段得知别人的真实出价,从根本上消除狙击。
所有创建、报名、出价、比价、结算动作都是链上交易;前端仅在浏览器 localStorage 保存 salt 与价格秘密,用于揭露阶段本地拼装,绝不上传服务器。
- 5 轮密封竞价:每轮 30s 提交哈希 → 30s 揭露原值,递进式逼近成交价。
- 待竞拍物品展示横廊:列表页底部内置 12 件黑金风物品图鉴(⌚百达翡丽 / 💎粉钻 / 🖼️加密艺术 / 🏎️法拉利 …),纯前端渲染,未连钱包也能浏览。
- 链上活动日志:
useWatchContractEvent+ 历史getLogs,把报名 / 出价 / 比价 / 成交 / 结算事件串成实时时间线。 - 每轮实时报价榜:揭露期按已揭露报价排序并标出当前领先者。
- 我的参与状态条 + 交易状态 Toast:pending 转圈、成功带 Monad 浏览器链接、失败红字详情。
- 黑金视觉语言:暗色网格纹理 + 顶部金色光带 + 渐变标题 + 阶段色卡片边框 + 悬停展开(最近成交价 / 最高出价)。
- 房间页物品大卡头图:巨型 emoji 水印 + 物品名 + 「🎯 正在竞拍」标识,操作台与结算面板均点明当前竞拍物品。
| 竞拍场列表(含待竞拍物品横廊) | 房间页(已结算) | 房间页(进行中) |
|---|---|---|
![]() |
![]() |
![]() |
| 层 | 技术 |
|---|---|
| 智能合约 | Solidity ^0.8.24 · Hardhat · OpenZeppelin 风格安全校验 |
| 前端 | Next.js 14(App Router)· React 18 · TypeScript · Tailwind CSS |
| 链上交互 | wagmi v2 · viem · TanStack Query |
| 钱包 | MetaMask(注入式 injected connector) |
| 数据 | 全部来自链上事件,无后端数据库 |
monad-auction/
├── contracts/ # Hardhat 合约工程
│ ├── contracts/MonadAuctionHouse.sol # 密封竞拍核心合约
│ ├── scripts/deploy.js # 部署脚本
│ ├── test/*.test.js # 16 个单元测试
│ └── hardhat.config.js # localhost / monadTestnet / monadMainnet
├── web/ # Next.js 前端
│ ├── app/page.tsx # 竞拍场列表 + 创建竞拍
│ ├── app/auction/[id]/page.tsx # 竞拍房间(黑金风格)
│ ├── lib/auction.ts # hash / 盐 / 精度计算(与合约字节级一致)
│ ├── lib/contract.ts # ABI、地址、常量、元组类型
│ ├── lib/items.ts # 待竞拍物品图鉴
│ └── components/ # Navbar / Countdown / RulesCard / providers
├── seed-samples.cjs # 注入 13 场样例竞拍
├── seed-items.cjs # 追加 6 场「待竞拍」物品样例
├── drive-demo.cjs # 将某场推进到丰富状态
└── docs/screenshots/ # README 用实拍图
- 密封出价:
hash = keccak256(abi.encodePacked(price, bidder, salt))。salt为固定 18 字节(bytes18),字母 / 数字 / 符号标点均可,多字节字符(中文 / emoji)会被前端拒绝。
- 押金:报名需质押 100,000 MON(卖家创建竞拍时同样质押)。
- 轮次:共 5 轮,人数不限。
- 每轮:30s 提交 price hash → 30s 揭露原值;30s 未揭露直接进入比价,任何人可在 30s 后发起比价(
compareRound)。 - 出价约束:
上一轮价格 ≤ 本轮出价 ≤ 最高价格 ≤ 100,000。
- 每轮:30s 提交 price hash → 30s 揭露原值;30s 未揭露直接进入比价,任何人可在 30s 后发起比价(
- 比价成交:第一名出价 ≥ 第二名 2 倍 → 直接拍下(若第 5 轮无 2 倍关系也直接成交)。
- 结算:拍下后卖家揭露实际价格。
- 第一名出价 < 实际价格 → 卖家支付差额;
- 第一名出价 ≥ 实际价格 → 第一名支付差额给卖家。
- 保证金退款(结算后自动退还):
- 买家退款 =
C + actualPrice − 2·winningBid - 卖家退款 =
C + 2·winningBid − actualPrice - 卖家超时未揭露 → 保证金罚没给买家(
finalizeNoReveal)。
- 买家退款 =
- 惰性推进:报名满 2 人记录
startAt = now + 30s,之后任何交互在超过该时间点后自动把阶段从Open推进到Live,无需定时器。
# 1. 启动本地节点(给 30 个账号各 1000 万 MON)
cd contracts
npx hardhat node
# 2. 新终端部署合约
cd contracts
npx hardhat run scripts/deploy.js --network localhost
# => 记下输出地址,如 0x5FbDB2315678afecb367f032d93F642f64180aa3
# 3. 前端指向合约
cd web
echo "NEXT_PUBLIC_CONTRACT_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3" > .env.local
# 4. 构建并启动
npm install
npm run build && npm run start
# 浏览器打开 http://localhost:3000,MetaMask 切到 Localhost:8545 (chainId 31337)注意:MetaMask 需要导入 hardhat 节点打印的私钥之一,并添加自定义网络
http://127.0.0.1:8545/ chainId31337。
启动空链后列表为空。运行以下脚本可批量注入样例竞拍,便于直接体验完整流程:
cd monad-auction
node seed-samples.cjs # 注入 13 场样例(含已结算 / 进行中 / 报名中)
node seed-items.cjs # 追加 6 场「待竞拍」物品样例(#14–#19)
node drive-demo.cjs # 将某场推进到「Live 第1轮双方已揭露」的丰富状态- 三个脚本均使用「关闭自动挖矿 + 本地自增 nonce + 手动
evm_mine」的确定性时序,避免 nonce 错乱。 - 演示账号(Hardhat 内置):#0 卖家
0xac09...ff80、#1 买家0x59c6...690d、#3 买家0x7099...。
若 next build 在结尾清理临时目录时中断,可改用独立构建目录绕过:
cd web
NEXT_DIST_DIR=.next-demo npx next build
NEXT_DIST_DIR=.next-demo npx next start -p 3000# contracts/.env
PRIVATE_KEY=你的私钥
MONAD_TESTNET_RPC=https://testnet-rpc.monad.xyz
cd contracts
npx hardhat run scripts/deploy.js --network monadTestnet
# web/.env.local
NEXT_PUBLIC_CONTRACT_ADDRESS=<上一步输出的地址>
cd web
npm run build && npm run start- 测试网 chainId
10143;主网 chainId143(--network monadMainnet)。 - 测试网水龙头:https://testnet.monad.xyz/faucet。
- 区块浏览器:https://testnet.monadexplorer.com。
- 合约 16 个 Hardhat 测试全部通过。
- 前端
computeBidHash与 Solidityabi.encodePacked(uint256, address, bytes18)哈希字节级一致(20 组随机盐含!@#$%^&*_-=+全部匹配)。 - 精度解析
parseMon与ethers.parseEther精确一致(BigInt 全程,无浮点误差)。 - 本地节点 E2E 冒烟:创建 → 双人报名 → 惰性开局 → 5 轮完整出价 / 揭露 / 比价 → 卖家揭露 → 结算,退款余额分毫不差。
MIT


