教程区块链区块链技术ch1818.6 NFT 市场:订单簿、版税与撮合引擎

本页目录

NFT 市场(如 OpenSea / Blur / LooksRare)的核心是一个链上/链下混合的订单撮合系统:卖方上架(NFT + 期望价格),买方按价成交,平台在成交时自动拆分版税(Royalty)给创作者。


18.6.1 市场架构:两种撮合模型

graph TD
    Seller[卖方] --> List[上架 Listing]
    Buyers[买方] --> Order[报价 Offer]
    List --> Engine{撮合引擎}
    Order --> Engine
    Engine -->|链下+签名| OB[链下订单簿<br/>订单以 EIP-712 签名,<br/>成交时上链执行]
    Engine -->|完全链上| OnChain[链上订单<br/>订单存于合约 storage]
    OB --> Match[成交执行]
    OnChain --> Match
    Match --> Royalty[版税拆分]
    Match --> Escrow[资金结算]
模型订单存储Gas灵活性代表
链下签名订单(Off-chain order)链下数据库,链上仅校验签名仅成交付 Gas高(取消免费、可批量)OpenSea / Blur / LooksRare
链上订单(On-chain listing)合约 storage上架/成交都付 Gas低(但更去信任)早期 CryptoPunks / 部分 1/1 平台

链下订单的信任假设:平台数据库不可被篡改地保存签名,但攻击者理论上可以重放签名——因此链上必须校验 nonce(订单序号)与截止时间,防止过期或已取消订单被执行。


18.6.2 链下签名订单:EIP-712 与成交校验

一张"链下订单"本质是一段被签名者签名的结构化数据。成交时,合约校验:

  1. 签名者确实是 NFT 当前 owner;
  2. 订单未过期(deadline > now)且 nonce 未使用;
  3. 价格不低于 price 且买方已支付;
  4. 版税按 creatorFeeBps 拆分给创作者(如有)。
typescript
// 链下订单撮合:签名校验 + 版税拆分的数学(纯 TS + BigInt,无外部库)
type Address = string;

interface Listing {
  seller: Address;      // 卖家
  nft: Address;         // NFT 合约地址
  tokenId: bigint;      // 具体 NFT 编号
  price: bigint;        // 成交价(wei)
  royaltyBps: bigint;   // 版税(1/10000)
  deadline: bigint;     // 截止时间戳
  nonce: bigint;        // 防重放序号
}

class Marketplace {
  usedNonces: Set<string>; // 已消费的 (seller:nft:tokenId:nonce)
  creatorRoyalty: Map<string, bigint>; // nft合约 => 版税bps(项目方预设)

  constructor() {
    this.usedNonces = new Set();
    this.creatorRoyalty = new Map();
  }

  // 成交结算:返回 [买方支付, 卖家所得, 创作者版税]
  fillOrder(
    order: Listing,
    fundsDeposited: bigint,
    _signatureValid: boolean
  ): [bigint, bigint, bigint] {
    if (!_signatureValid) throw new Error("签名无效");
    if (fundsDeposited < order.price) throw new Error("支付不足");
    const nonceKey = `order.seller:{order.seller}:{order.nft}:order.tokenId:{order.tokenId}:{order.nonce}`;
    if (this.usedNonces.has(nonceKey)) throw new Error("订单已执行/取消");
    this.usedNonces.add(nonceKey);

    const royalty = (order.price * order.royaltyBps) / 10000n; // 版税
    const sellerNet = order.price - royalty;                    // 卖家净得
    return [order.price, sellerNet, royalty]; // [买方支付, 卖家, 创作者]
  }
}

// 验证:售价 1 ETH,版税 5% => 版税 0.05 ETH,卖家净得 0.95 ETH
const WEI = 10n ** 18n;
const mk = new Marketplace();
const order: Listing = {
  seller: "0xSeller",
  nft: "0xNft",
  tokenId: 1n,
  price: 1n * WEI,
  royaltyBps: 500n, // 5%
  deadline: 2000n,
  nonce: 0n,
};
const [pay, seller, creator] = mk.fillOrder(order, 1n * WEI, true);
console.log(
  "版税 =", Number(creator) / Number(WEI), "ETH,",
  "卖家净得 =", Number(seller) / Number(WEI), "ETH"
);
// 输出:版税 = 0.05 ETH  卖家净得 = 0.95 ETH

18.6.3 版税的实现与争议

版税(Royalty)在成交时强制拆分给创作者。历史上由市场自觉执行,2023 年后各平台政策分化(OpenSea 默认强制、Blur 部分可选、部分平台设上限)。

方案谁执行优点缺点
市场级强制市场合约简单、创作者收益稳定平台可关闭;不同市场规则不一
合约级 ERC-2981NFT 合约 royaltyInfo()链上原语,不可绕过需 NFT 合约支持
可编程版税(如 on-chain royalties / 黑名单市场)转移时校验最强保障影响互操作性、抗 FUD

ERC-2981 接口:

solidity
interface IERC2981 {
    /// @notice 返回给定 tokenId/售价下的版税接收者与金额
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external view returns (address receiver, uint256 royaltyAmount);
}

18.6.4 攻击面与安全清单

风险描述防御
签名重放同一订单在另一市场重复执行nonce + 市场地址进入签名域(EIP-712 domain)
订单过期卖家忘了下架,价格已失真deadline 校验
版税绕过跨平台/私下交易绕过版税ERC-2981 + 市场强制
虚假 NFT 合约高仿同名合约钓鱼合约地址白名单校验
前端钓鱼恶意签名(授权/上架)请求明确提示每次签名含义
礼包/批量成交偏差多 token 打包成交价格偏差单笔逐项校验

18.6.5 小结

3 个关键认知:① NFT 市场 = 签名订单 + 链上成交校验 + 版税拆分三件事;② 链下订单省 Gas 但信任平台,链上订单去信任但贵;③ 版税是创作经济的核心,需从"市场自觉"走向"合约原语"。


, 前往 → ch18 总结 |*

评论

0

评论加载中…

发表评论

0/2000