挂单写进合约:CryptoPunks 市场合约里的两张表与提款式结算 图 1
挂单写进合约:CryptoPunks 市场合约里的两张表与提款式结算 · 图 1

现在买卖 NFT,通常是把藏品挂到某个市场,撮合由平台完成。但在以太坊 NFT 的早期,撮合本身是一段合约:CryptoPunks 官方市场合约把挂单、出价、成交、结算全部写进了链上状态。逐段读完这份源码,能看清“链上市场”在最朴素形态下的规则长什么样,也能理解为什么这些老规则常常和今天的直觉对不上。

合约里的核心结构是两张表。第一张记录“谁把哪只藏品挂出来卖”,每只藏品对应一条出售信息:是否在挂、卖家地址、最低可卖价,以及一个可选字段——只卖给某一个指定地址。第二张记录“这只藏品当前最高的一笔出价”,注意是单数结构:每只藏品同时只保留一笔出价,不是一摞出价。这两张表都是公开可读的合约状态,谁挂了、挂了多少钱,链上直接查。

出售一侧有三个入口。挂单时写下最低可卖价即可;也可以指定买家,指定之后除该地址外任何人调用买入都会被拒绝;不想卖了则把这条出售记录清空。这里没有“撤单要付手续费”的概念,因为改动的只是自己的状态。

出价一侧的规则最值得注意。合约要求新出价的金额必须严格大于当前那笔出价,否则整笔调用失败。成功时,旧出价人的钱不会被退回他的钱包,而是转入合约为他准备的待提款额度,由他日后主动取回。这意味着:被高价顶掉的人如果不知道要提款,那笔钱就一直躺在合约里。它不是丢了,但也不会自动回家。买家因此需要记住两条:出价是被动的资金占用,被超后需要自己动作;合约不会替你催款。

成交有两条路径。第一条:卖家已经挂单,买家调用买入函数并附上足够的钱。合约会逐项检查:这只藏品是否处于挂售状态、有没有被指定给他人、你付的钱是否达到最低价、挂单的人是否仍是当前持有人。任何一条不满足就整笔失败。全部通过则藏品直接过户给买家,成交款进入卖家的待提款额度,同样需要卖家自己来取。合约在这里还做了一个贴心的收尾:如果新买家自己恰好也对该藏品留有出价,他那笔出价会被就地注销、款项转入他的待提款额度——自己买下的东西不用再给自己出价。第二条路径:卖家没有挂单,但有人留了出价,持有人可以主动接受这笔出价,并设置一个“最低接受价”作为兜底,防止点击瞬间出价被人垫高。接受成交后藏品过户给出价人,款项入账,那笔出价随即清空。

把“钱进入待提款额度、要自己取回”这套设计看仔细,它叫拉款模式:合约不主动给任何外部地址打钱,只记账,收款方自己发起取款。这既是安全取舍(避免合约在成交那一笔里把资金转给一个可能挂起回调的外部合约,源码里甚至留了防重入的注释),也是体验代价——早期很多人确实忘了这里还有一笔钱可提,界面上并不显眼。读懂这份合约的人,会把“我在某合约里有未提余额”当成一项日常检查。

与今天的订单簿市场对比,差异结构性地存在。现代市场把订单放在链下数据库,靠平台索引撮合,成交才落链,所以能支持多档深度、部分成交、复杂优先级;老合约市场把订单写进世界状态,每一笔挂单都要付 Gas,规则只能极简:每只藏品一个挂单位、一笔出价位,超出即覆盖。两种模式对买家的实际含义完全不同——在老式合约市场里,你的出价能否成交取决于持有人是否点头,价格优先只体现在“必须高过前任”,时间顺序不构成任何权利。

本文为机制说明,不构成任何投资建议。

挂单写进合约:CryptoPunks 市场合约里的两张表与提款式结算 图 2
挂单写进合约:CryptoPunks 市场合约里的两张表与提款式结算 · 图 2