近期,NFT 市场 OpenSea 宣布推出全新 Web3 市场协议 Seaport 协议,用于安全高效地买卖 NFT。本文将深度分析其关键业务实现和接口实现。
近期,NFT 市场 OpenSea 宣布推出全新 Web3 市场协议 Seaport 协议,用于安全高效地买卖 NFT。本文将深度分析其关键业务实现和接口实现。SeaPort官方文档 https://docs.opensea.io/v2.0/reference/seaport-overview , 可配合查阅,进一步加深理解。
Uniswap 用开源去中心化交易改变了加密货币交易的游戏规则,这是我们现在所知的 2020 年 DeFi Summer的开始,也带来了 DEX 和 DeFi 的大规模增长和创新 。OpenSea的新协议Seaport或许也有改变NFT交易游戏规则的潜力,这也是我们分析Seaport协议的原因。
Seaport 是一个市场合约,用于安全有效地创建和执行 ERC721 和 ERC1155 代币的订单。 每个订单包含任意数量的供应商愿意提供的物品(“报价(offer)”)以及任意数量的必须连同其各自的接收者一起接收的物品(“对价(consideration)”)。Seaport 协议的 6 大关键点,以及它对 NFT 领域的意义: (1)开源代码: 有了 Seaport 协议,任何人都可以使用该协议构建一个 NFT 市场,因为它是去中心化和开源的。在未来几年,我们应该会看到更多的 NFT 市场建立起来。更多的竞争=更好+更快的创新 (2)去中心化: OpenSea 说这个协议没有合约所有者,任何人都可以更新或生成代码。 (3)交易新范式: 与一些平台只能用加密货币换取 NFT 不同,Seaport 协议允许用户以一系列新方式获取 NFT,投标人(或报价者)可以捆绑不同的资产(如提供 ETH/ERC20/ERC721/ERC1155 资产)以换取 NFT。 (4)交易特定的 NFT: 当交易 NFT 时,你也可以设置 NFT 必须具备的特定“条件”。 (5)荷兰式拍卖列表: 在 Seaport 协议中,你可以设置一个开始和结束价格,表明你希望拍卖持续多长时间。该列表将降低(或提高)价格,直到找到买家(或拍卖时间到)。 (6)更高的安全性: OpenSea 正在进行为期两周的协议审计竞赛,奖金总额为 100 万美元。任何开发人员都可以审核代码,提交他们发现的评审和错误,并获得奖励。
每一个订单都包含11个关键组件:
offerer
订单的报价者提供了所有的供应代币并且必须亲自执行订单(即 msg.sender == offerer
)或者通过签名(标准的65字节 ECDSA,64字节 EIP-2098 或 EIP-1271isValidSignature
检查)或列举链上订单(即调用 validate
)来批准订单。zone
订单的区域是附加到订单的可选辅助帐户,具有两个额外的权限:
cancel
来取消命名为该区域的订单。(注意,报价者仍可以取消他们自己的订单,可以单独取消,也可以通过调用 incrementNonce
立刻取消由其当前 nonce 签名的所有订单)。isValidOrder
或 isvalidOrderIncludingExtraData
视图函数来获得批准。offer
报价包含可以从报价者帐户转移的一系列代币,其中每个代币由以下组件组成:
itemType
指定代币类型,有效类型包括 Ether(或者其他指定链的原生代币)、ERC20、ERC721、ERC1155、ERC721、有“条件(criteria)”的 ERC721 以及有“条件(criteria)”的 ERC1155。token
指定代币合约的账户地址(空地址用于以太币或其他原生代币)。identifierOrCriteria
表示 ERC721 或 ERC1155 代币标识符,或者在基于条件的代币类型的情况下,表示由代币的有效代币标识符集合组成的 merkle 根。对于 Ether 和 ERC20 类型 ,该值会被忽略,并且对于基于条件的代币类型,可以将值设置为 0 以允许任何标识符。startAmount
表示如果在订单激活时完成订单所需要的相关代币的数量。endAmount
表示如果在订单到期时执行订单所需要的相关代币的数量。如果此值与 startAmount
不同,则根据订单激活后经历的时间线性计算出实际的数量。consideration
包含为完成订单而必须接收的代币数组。它包含所有与所提供代币相同的组件,并且还包括一个用于接收每个代币的 recipient
组件。该数组可以由执行者在订单执行时进行扩展,以支持“小费”(例如中继费或推荐费)。orderType
订单类型,根据两个不同的偏好,指定订单的四种类型之一,:
FULL
表示不支持部分填充,而 PARTIAL
允许填充订单中的一部分,注意每个代币必须被提供的分数完全整除(即除法后没有余数)。OPEN
表示任意账户都可以提交执行订单的调用,而 RESTRICTED
则需要订单必须由报价者或订单所在区域执行,或者在区域上调用 isValidOrder
或 isValidOrderIncludingExtraData
视图函数时返回表示订单被批准的神奇的值。startTime
表示订单激活时的区块链时间。endTime
表示订单到期的区块链时间。该值与 startTime
与每个代币的 startAmount
和 endAmount
一起使用以得出它们的当前数量。zoneHash
表示一个任意的 32 字节值,当执行受限订单时,该值将提供给区域,该区域在确定是否是授权订单时可以使用该值。salt
表示订单的任意熵源。conduitKey
是一个 bytes32
类型的值,表示在执行转移时应将哪个渠道(conduit)(如果有)用作代币批准的来源。默认情况下(即当 conduitKey
设置为零哈希时),报价方将直接向 Seaport 授予 ERC20、ERC721 和 ERC1155 代币批准,以便它可以在执行期间执行订单指定的任何转移。相反,选择使用渠道的报价者将授予与提供的渠道密钥相对应的渠道合约的代币批准,然后 Seaport 指示该渠道转移相应的代币。nonce
表示必须与给定报价者的当前随机数匹配的值。订单通过以下4种方式中的一种来执行:
fulfillOrder
和 fulfillAdvancedOrder
中的一个,并且构造第二个隐含订单,同时其调用者作为报价者(offerer),已执行订单的对价(consideration)作为报价(offer),已执行订单的报价作为对价(使用“高级”订单包含应与一组“条件解析器”一起填写的部分,这些“条件解析器”为已执行订单上的每个基于条件的代币指定一个标识符和相应的包含证明)。所有报价代币将从订单报价者转移到执行者,然后所有对价代币将从执行者转移到指定的接收者。fulfillBasicOrder
,并提供六种基本路线类型(ETH_TO_ERC721
、ETH_TO_ERC1155
、ERC20_TO_ERC721
、ERC20_TO_ERC1155
、ERC721_TO_ERC20
以及 ERC1155_TO_ERC20
)中的一种,将从组件子集派生要执行的订单,假设相关订单符合以下条件:
startAmount
必须与该项目的 endAmount
匹配(即项目不能有升序/降序数量)。token
和原生代币项目中的 identifierOrCriteria
以及 ERC20 项目中的 identifierOrCriteria
)都设置为空地址或零。1
fulfillAvailableOrders
和 fulfillAvailableAdvancedOrders
)中的一个,并且提供一组订单与一组执行声明,其中的执行声明指定哪些报价项目可以聚合到不同的转移中,相应地哪些对价项目可以聚合在一起,以及其中已经取消的订单是因为时间无效,或者已经完全成交的订单将被跳过,而不会导致其余可用订单回滚。此外,一旦锁定 maximumFulfilled
可用订单,剩余的所有订单都将被跳过。与标准执行函数类似,所有报价项目将从各自的报价者转移到执行者,然后所有对价项目将从执行者转移到指定的接收者。matchOrders
和 matchAdvancedOrders
)中的一个,并且提供一组明确的订单以及一组执行,该执行指定了哪些报价项目应用于哪些对价项目(并且“高级”案例以类似的方式运行标准方法,但支持通过提供的分子 numerator
和分母 denominator
小数值以及可选的 extraData
参数进行部分填充,当执行受限订单类型时,这些参数将作为调用区域上的 isValidOrderIncludingExtraData
视图函数的一部分提供)。 请注意,以这种方式执行的订单没有明确的执行者; 相反,Seaport 将简单地确保每个订单的需求一致。虽然标准方法在技术上可用于执行任何订单,但在某些情况下存在关键的效率限制:
创建报价时,应检查以下要求以确保订单可以执行:
执行基本订单时,需要检查以下要求以确保订单可以执行:
msg.value
执行标准订单时,需要检查以下要求以确保订单可以执行:
msg.value
在执行一组匹配订单时,需要检查以下要求以确保订单可以执行:
msg.value
的形式提供. 请注意,提供者和接收者是同一帐户的执行将从最终执行集中被过滤掉。在构建订单时,报价者可以选择通过设置适当的订单类型来启用部分成交。然后,支持部分执行的订单可以在相应订单的某一部分中执行,从而允许后续执行绕过签名验证。总结一下部分填充的几个关键点:
startAmount
和 endAmount
(例如,它们是递增数量或递减数量的项目),则在确定当前价格之前,该分数将应用于这两个数量。这确保了在构建订单时可以选择完全可分的金额,而不依赖于最终完成订单的时间。当通过 fulfillOrder
或 fulfillAdvancedOrder
来执行订单时:
offerer
或者 zone
,调用 zone
判断订单是否有效startAmount
和结束金额 endAmount
OrderFulfilled
事件
当通过 matchOrders
或者 matchAdvancedOrders
来匹配一组订单时,步骤 1 到 6 几乎完全相同,但针对每个提供的订单执行。从这里开始,执行与上面的标准执行不同:
应用执行
作为每次执行的一部分进行转账
to == from
或 amount == 0
时的每次执行(注意:当前实现不执行最后一次优化)Seaport 是一个通用的 ETH/ERC20/ERC721/ERC1155 市场。它最大限度地减少了外部调用,并为普通路由提供了轻量级的方法,以及更灵活的方法来组合高级订单。
ConsiderationInterface 包含 Seaport 的所有外部函数接口。
执行基本订单,仅支持Ether(或指定链的原生代币)与 ERC721 之间的交易。
准备执行基本订单
(1)添加重入锁
(2)校验时间正确
(3)检验参数正确
(4)计算并校验订单的哈希值
(5)更新订单状态
_validateOrderAndUpdateStatus
:根据参数,验证订单,更新状态并计算订单哈希值 orderHash
、需要执行订单的分子 numerator
和分母 denominator
(1)时间校验:startTime <= block.time <= endTime
(2)分子与分母校验:
numerator <= denominator && denominator != 0
;numerator == denominator
,需要支持部分执行(Support Partial Fills)(3)对价项目长度校验以及计算订单哈希值 orderHash
:
orderParameters.consideration.length >= orderParameters.totalOriginalConsiderationItems
,即参数中 consideration
数组实际长度要大于或等于参数中直接传递的原始对价项目总数orderHash
(4)校验高级订单的有效性:订单类型为 2 或 3 要求 zone 或 offerer 是 caller 或 者 zone 批准。
(5)校验订单状态 orderStauts = _orderStatus[orderHash]
,保证订单没有被取消并且是可执行的
orderStauts.isCancelled == false
orderStatus.numerator != 0
,保证 orderStatus.numerator < orderStatus.denominator
。(6)校验订单签名,即若orderStatus.isValidated == false
,则调用 _verifySignature
函数
(7)计算需要执行订单的分子 fillNumerator
和分母 fillDenominator
(8)更新状态变量 orderStatus
_applyCriteriaResolvers
:用条件解析器,对每个生成的订单类型以及条件解析器的绑定进行校验,确保提交的待执行订单是有效的_applyFractionsAndTransferEach
:以指定的分数值执行每个项目的转账_emitOrderFulfilledEvent
:发出一个表明订单已完成的事件。_validateOrdersAndPrepareToFulfill
:校验订单(若有无效订单则跳过)、更新其状态、通过先前填充的分数减少金额、应用条件解析器并触发 OrderFulfilled 事件。
(1)添加重入锁
(2)声明并设置一个错误缓冲区变量,指明任何本地报价项目的状态。
(3)循环遍历每一个订单,校验订单,更新每一个订单中的参数:
_validateOrderAndUpdateStatus
:根据参数,验证订单,更新状态并计算订单哈希值 orderHash
、需要执行订单的分子 numerator
和分母 denominator
startAmount
和 endAmount
startAmount
和 endAmount
(4)校验错误缓冲区变量
(5)_applyCriteriaResolvers
:应用条件解析器,对每个生成的订单类型以及条件解析器的绑定进行校验,确保提交的待执行订单是有效的聚合已使用的报价和对价项目并执行转账
(6)触发 OrderFulfilled 事件
_executeAvailableFulfillments
:完全或部分执行通过了校验的订单,每个订单具有任意数量的要约和考虑项目,并执行转移。 任何当前未激活、已完全成交或已取消的订单都将被忽略。 然后,剩余的报价和考虑项目将在可能的情况下聚合,如所提供的报价和考虑组件数组所示,并且聚合的项目将分别转移到履行者或每个预期的接收者。 请注意,失败的项目转移或订单格式问题将导致整个批次失败。
(1)为每一个订单的报价和对价项目的执行分配一个 Execution
结构,构造为一个 Execution
结构数组 executions
,任何当前未激活、已完全成交或已取消的无效订单都将被忽略。
(2)校验 executions
的长度
(3)对 executions
中的每一个 Execution
结构对象进行校验,过滤掉当前未激活、已完全成交或已取消的所有订单。
(4)_performFinalChecksAndExecuteOrders
:对高级订单以及 executions
进行最后的校验,然后执行订单,完成执行转账,删除重入锁。
matchOrders
:对普通订单中的报价和对价项目按参数 fulfillments
提供的报价组件分配给对价组件的条件求进行匹配然后执行,不支持订单部分执行,不支持条件解析器;普通订单作为特殊的高级订单进行处理matchAdvancedOrders
:对高级订单中的报价和对价项目按参数 fulfillments
提供的报价组件分配给对价组件的要求进行匹配然后执行_validateOrdersAndPrepareToFulfill
:校验订单(若有无效订单则回滚)、更新其状态、通过先前填充的分数减少金额、应用条件解析器并触发 OrderFulfilled 事件。若有无效订单,则回滚。_fulfillAdvancedOrders
:在验证、调整金额和应用条件解析器之后,执行高级订单
(1)为每一个订单的报价和对价项目的执行分配一个 Execution
结构,构造为一个 Execution
结构数组 executions
(2)循环遍历参数 fulfillments
,将 executions
中的每一个元素对应到fulfillments
的每一个元素,若报价人者(offerer
)和接受者(recipient
)是相同的,则跳过。
(3)_performFinalChecksAndExecuteOrders
:对高级订单以及 executions
进行最后的校验,然后执行订单,完成执行转账,删除重入锁。
SharkTeam的愿景是全面保护Web3世界的安全。团队成员分布在北京、南京、苏州、硅谷,由来自世界各地的经验丰富的安全专业人士和高级研究人员组成,精通区块链和智能合约的底层理论,提供包括智能合约审计、链上分析、应急响应等服务。已与区块链生态系统各个领域的关键参与者,如Huobi Global、OKC、polygon、Polkadot、imToken、ChainIDE等建立长期合作关系。
Telegram:https://t.me/sharkteamorg
Twitter:https://twitter.com/sharkteamorg
Reddit:https://www.reddit.com/r/sharkteamorg
更多区块链安全咨询与分析,点击下方链接查看
D查查|链上风险核查 https://m.chainaegis.com
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!