跳到主要内容

Exchange Protocol

源码

合约地址: 0x7e832eC8ad6F66E6C9ECE63acD94516Dd7fC537A

交易合约主要负责订单撮合。

订单的数据结构

订单分为挂单 Maker 和吃单(成交单) Taker。 挂单发生在链下,用户经过 EIP712 进行签名后即可挂单。当有吃单时,会将挂单相关的签名参数和吃单参数一起传入该合约中进行订单撮合,并完成后续操作,包括 nft 转移等。

订单的数据结构分为 MakerOrderTakerOrder

// 挂单 - 可以是挂买单或卖单
struct MakerOrder {
bool isOrderAsk; // 挂单方向, ask/卖时为true, bid/买时为false
address maker; // 挂单人的地址
address collection; // nft合约地址
uint256 price; // 价格
uint256 tokenId; // 挂单的nft tokenId
uint256 amount; // 挂单的数量, erc721时为1,,erc1155时可以>=1
address strategy; // 交易执行策略
address currency; // 币种
uint256 nonce; // 订单随机数(必须唯一,除非是为了覆盖现有的挂单,例如降低卖价)
uint256 startTime; // 开始时间
uint256 endTime; // 结束时间
uint256 minPercentageToAsk; // 滑点
bytes params; // 附加的参数
address interceptor; // 拦截器地址
bytes interceptorExtra; // 拦截器参数
uint8 v; // v: parameter (27 or 28)
bytes32 r; // r: parameter
bytes32 s; // s: parameter
}
struct TakerOrder {
bool isOrderAsk; // ask(卖)为true, bid(买)时为false
address taker; // msg.sender
uint256 price; // 购买价格
uint256 tokenId; // tokenId
uint256 minPercentageToAsk; // 滑点
bytes params; // 附加的参数
address interceptor; // 拦截器地址
bytes interceptorExtra; // 拦截器参数
}

订单撮合

当有吃单发生时,吃单发起者需要根据条件调用下面的方法完成交易:

matchAskWithTakerBid

挂单是卖单, 吃单为买单

function matchAskWithTakerBid(
OrderTypes.TakerOrder calldata takerBid,
OrderTypes.MakerOrder calldata makerAsk
) external override nonReentrant {}

方法内部流程如下

  • 验证 maker 方向是卖 和 taker 方向是买
  • 方法的调用者必须是 taker
  • 订单签名验证
  • 验证 maker 下的交易策略是否可执行
  • 发送交易费到指定地址
  • 发送 NFT
  • 转账到 maker

matchBidWithTakerAsk

挂单是买单, 吃单为卖单

function matchBidWithTakerAsk(
OrderTypes.TakerOrder calldata takerAsk,
OrderTypes.MakerOrder calldata makerBid
) external override nonReentrant {}

方法内部流程如下

  • 验证 maker 方向是买 和 taker 方向是卖
  • 方法的调用者必须是 taker
  • 订单签名验证
  • 验证 makerOrder 下的交易策略是否可执行
  • 发送交易费到指定地址
  • 发送 NFT
  • 转账到 taker

matchAskWithTakerBidUsingETHAndWETH

挂单是卖单, 吃单为买单, 且支付方式为 ETH

function matchAskWithTakerBidUsingETHAndWETH(
OrderTypes.TakerOrder calldata takerBid,
OrderTypes.MakerOrder calldata makerAsk
) external payable override nonReentrant {}

相比于方法 matchAskWithTakerBid 方法多了一步验证

require(makerAsk.currency == WETH || makerAsk.currency == address(0), "Order: currency must be WETH or ETH");

即 挂单的 currency 必须是 WETH 或是 ETH

交易策略

MakerOrder 结构中的 strategy 字段存储的是交易策略的合约地址。

有以下几种交易策略

  • StrategyAnyItemFromCollectionForFixedPrice: 以固定的价格执行交易, 可以对集合内的任意 tokenID 吃买单

  • StrategyAnyItemInASetForFixedPrice: 以固定的价格执行交易, 可以对集合内的某些 tokenID 吃买单

  • StrategyDutchAuction: 拍卖时执行的策略。拍卖价格由时间确定

  • StrategyPrivateSale: 只有 maker 指定的 taker 才能执行交易, taker 地址存储在 makerOrder 中的 params 字段中

  • StrategyStandardSaleForFixedPrice: 以固定的价格执行交易

交易策略其实是对订单参数的在特殊条件下的额外验证。交易合约中有 executionManager 全局变量,在进行撮合交易时,根据 makerOrder 中的 strategy 的值,构造交易策略的实例并进行一些特殊的验证。