一单完整的交易必须存在2种角色:maker和taker。
maker :账本深度的制造者。
taker:账本深度的移除者。在DEX中它扮演者最重要角色,它永远是交易的推动者。
交易中还有2种行为:ask和bid
ask:卖方出价,用NFT换取token。
bid:买方出价,用token购买NFT。
以上两两组和之后就会形成一下的成对交易方式:
MakerAsk -- TakerBid:买家出售NFT以换取相应的Token,卖家拿Token换取NFT。
MakerBid --TakerAsk:买家以token申购NFT,卖家接受买家报价并执行交易和兑换。

User1拥有两个NFT,他对NFT1有卖出意向,于是在交易所中进行了挂单。
User2对NFT1感兴趣,用手里的token购买了NFT1。
User2是交易的实际发起者,他将负担交易的gas fee。
User1--maker,他的行为是ask。
User2--taker,他的行为是bid。
User1当前并未打算出售NFT2,所以他并未对其挂单。
User2对NFT2非常着迷,尝试对NFT1进行报价,用token求购之。
NFT2难以拒绝的报价让User1决定接受之。
User1是交易的实际发起者,他将负担交易的gas fee。
User1--taker,他的行为是ask。
User2--maker,他的行为是bid。

LooksRare总体来说分为三个核心子域,分别是
支付域
负责合法的交易token管理。
负责NFT交易的转账管理。
版权域
负责Collection版权费、平台佣金和用户的收益分成管理。
执行域
负责执行动作和执行策略的实施。

Looksrare交易部分当前由四大组件构成:货币管理器、NFT选择器、执行管理器、版权费管理器。
货币管理器
货币管理器主要功能是在Looksrare上可以进行交易的合法货币币种进行增加/移除/查询等操作。能执行该操作的人限定是Looksrare平台方。
NFT选择器
NFT选择器主要的职能是选择正确的
接口选择器(ERC721,ERC1155)调用safeTransferFrom方法。LooksRare针对非标准的NFT Collection也提供了相应的适配,不过它底层调用的是transferFrom方法存在一定安全风险。
版权域主要的职责是管理各个Collection的版权费计算器和设置相应的费率条件。
版费设置器
为Collection的Owner提供设置/修改Collection管理员、版费接收账户和版费,这构成了「版费对象」。
版费注册器
它的首要职能将collection与设置的自定义版费对象关联起来。其次它还负责运行时计算对应的版费。
个人觉得这里设计上还有优化空间。还应该增加一个版费计算器接口并实现之。这样注册和计算解耦,也为外部合作者提供了自定义版费计算方式的生态玩法。
版费管理器
它仅仅充当版权域的「聚合根」作用。
执行管理器
ask和bid的执行动作有不同的执行策略。执行管理器主要负责执行策略的增加/移除查询。官方实现的执行策略如下有:StrategyStandardSaleForFixedPrice
StrategyPrivateSale
StrategyDutchAuction
StrategyAnyItemInASetForFixedPrice
StrategyAnyItemFromCollectionForFixedPrice
struct MakerOrder {
bool isOrderAsk; // true --> ask【卖】 / false --> bid【买】
address signer; // signer of the maker order
address collection; // collection address
uint256 price; // price (used as )
uint256 tokenId; // id of the token
uint256 amount; // amount of tokens to sell/purchase (must be 1 for ERC721, 1+ for ERC1155)
address strategy; // strategy for trade execution (e.g., DutchAuction, StandardSaleForFixedPrice)
address currency; // currency (e.g., WETH)
uint256 nonce; // order nonce (must be unique unless new maker order is meant to override existing one e.g., lower ask price)
uint256 startTime; // startTime in timestamp
uint256 endTime; // endTime in timestamp
uint256 minPercentageToAsk; // slippage protection (9000 --> 90% of the final price must return to ask)
bytes params; // additional parameters
uint8 v; // v: parameter (27 or 28)
bytes32 r; // r: parameter
bytes32 s; // s: parameter
}
struct TakerOrder {
bool isOrderAsk; // true --> ask【买】 / false --> bid【卖】
address taker; // msg.sender
uint256 price; // final price for the purchase
uint256 tokenId;
uint256 minPercentageToAsk; // // slippage protection (9000 --> 90% of the final price must return to ask)
bytes params; // other params (e.g., tokenId)
}

必须满足以下条件:
maker是挂单者,售卖NFT;taker是吃单者,用token买NFT。
maker接受的货币类型必须是WETH。
调用方是taker。
付款金额不得大于NFT售价。
如果付款金额小于NFT售价,则尝试用msg.sender账户的WETH转移到该合约WETH账户。
订单信息校验又可以分为以下步骤:

a.基础校验
maker的该订单没有被执行过
maker的nonce不低于合约中user的最小执行nonce
maker发起者不是address(0)
maker的出售数额>0
b.maker签名校验
这里使用https://eips.ethereum.org/EIPS/eip-712 规范进行校验。
如果是调用方是合约,验证是否接口满足InterfaceSelector。
如果调用方是外部地址,则从hash中恢复出signer',判断外部传入signer == singer' 。
c.交易货币合法性校验
d.交易策略合法性校验
使用maker.strategy的地址所代表出售策略执行
IExecutionStrategy.canExecuteTakerBid(TakerOrder taker, MakerOrder maker),其中可以被maker事先选择执行的合法策略有:
StrategyStandardSaleForFixedPrice (固定价格销售)
{ return ( ((makerAsk.price == takerBid.price) && (makerAsk.tokenId == takerBid.tokenId) && (makerAsk.startTime <= block.timestamp) && (makerAsk.endTime >= block.timestamp)), makerAsk.tokenId, makerAsk.amount ); }StrategyPrivateSale(售卖给指定用户)
{ // 从maker签名中反解出指定售卖人 address targetBuyer = abi.decode(makerAsk.params, (address)); return ( ((targetBuyer == takerBid.taker) && //msg.sender必须为buyer才能进行下去 (makerAsk.price == takerBid.price) && (makerAsk.tokenId == takerBid.tokenId) && (makerAsk.startTime <= block.timestamp) && (makerAsk.endTime >= block.timestamp)), makerAsk.tokenId, makerAsk.amount ); }StrategyDutchAuction(荷兰式拍卖)
{ uint256 startPrice = abi.decode(makerAsk.params, (uint256)); //maker设定的起拍价 uint256 endPrice = makerAsk.price; //maker设定的底价 uint256 startTime = makerAsk.startTime; //maker设定的拍卖起止时间 uint256 endTime = makerAsk.endTime; require(endTime >= (startTime + minimumAuctionLengthInSeconds), "Dutch Auction: Length must be longer"); require(startPrice > endPrice, "Dutch Auction: Start price must be greater than end price"); //随着时间流逝,当前拍卖价格会逐渐接近于maker的底价 uint256 currentAuctionPrice = startPrice - (((startPrice - endPrice) * (block.timestamp - startTime)) / (endTime - startTime)); return ( (startTime <= block.timestamp) && //拍卖时间内才运行成交 (endTime >= block.timestamp) && (takerBid.price >= currentAuctionPrice) && //只要买家出价不小于当前拍卖喊价即可 (takerBid.tokenId == makerAsk.tokenId), makerAsk.tokenId, makerAsk.amount ); }
费用结算主要是对taker的付款金额按照协议费、版权费清算后再将剩余款项转给卖家。
1)协议费结算
协议费结算也与maker选定的执行策略先关,主要是使用
IExecutionStrategy.viewProtocolFee(),其中合法的策略如【2.1.5】阐述所示,其中对应的协议率均为各自contract部署的时候设置,且无修改接口。
协议费的计算公式是
协议费 = 交易金额 * 协议费率
协议费以WETH的形式转入交易所协议费收款账户protocolFeeRecipient,剩下的钱继续后续结算。from账户是合约WETH账户。
2)版权费结算
版权费的结算“费基” 也是原始交易金额。
版权费是结算给对应Collection方,并且各个Collection的版权费收款地址和费率各不相同。这些版权费信息被封装在一个个FeeInfo对象中,并且Collection与FeeInfo的映射关系在RoyaltyFeeRegistry的_royaltyFeeInfoCollection存放。
struct FeeInfo {
address setter; //版权费设置人
address receiver;//版权费收款人
uint256 fee;//版权费率
}
LooksRare未遵循ERC-2918协议,接口采用royaltyInfo(addresscollection, uint256amount) returns (address, uint256),返回合约版权费收款账户和版权费。
协议费的计算公式是
版权费 = 交易金额 * 版权费率
*如果Collection未在LooksRare设置版权费信息,则会尝试调用Collection实现的ERC2981协议计算版权费。
3)卖家结算
卖家收款金额 = 交易金额 - 协议费 - 版权费
卖家的结算也会以WETH的形式从交易所合约WETH账户转入。
1)选择转让管理器TransferManager
在之前被注册到交易所上的transferSelectorNFT中根据Collection地址寻找合适的转让管理器。Collection与transferManger地址的映射关系存放于transferManagerSelectorForCollection中。
如果Collection没有绑定对应的TransferManager,LooksRare会根据Collection实现的接口进行判断,如果实现了ERC721,则用TRANSFER_MANAGER_ERC721,如果实现ERC1155,则用INTERFACE_ID_ERC1155。
2)根据不同的transferManager,实际调用的是标准的IERC721/1155的safeTransferFrom方法。
与【2.1】重要区别是付款方式不再是ETH或WETH,而是另外一些ERC20。

必须满足以下条件:
maker是挂单者,售卖nft;taker是吃单者,用token买nft。
调用方是taker。
费用结算与【2.1.6】的协议费、版权费和卖家结算金额计算方式相同,唯一不同之处是采用maker认定的货币类型进行结算。
IERC20(currency).safeTransferFrom(from, protocolFeeRecipient, protocolFeeAmount);

maker是挂单者,用token买nft;taker是吃单者,nft换token。
taker是调用者
与之前相同。
使用maker.strategy的地址所代表申购策略执行。
IExecutionStrategy.canExecuteTakerAsk(TakerOrder taker, MakerOrder maker),其中可以被maker事先选择执行的合法策略有:
StrategyStandardSaleForFixedPrice (固定价格申购)
{ return ( ((makerBid.price == takerAsk.price) && (makerBid.endTime >= block.timestamp) && (makerBid.startTime <= block.timestamp)), takerAsk.tokenId, makerBid.amount ); }StrategyAnyItemInASetForFixedPrice(固定价格购买指定集合中的任意Item)
{ // Precomputed merkleRoot (that contains the tokenIds that match a common characteristic) bytes32 merkleRoot = abi.decode(makerBid.params, (bytes32)); // MerkleProof + indexInTree + tokenId bytes32[] memory merkleProof = abi.decode(takerAsk.params, (bytes32[])); // Compute the node bytes32 node = keccak256(abi.encodePacked(takerAsk.tokenId)); // Return whether the order can be executed, the tokenId, and the amount to sell return ( (MerkleProof.verify(merkleProof, merkleRoot, node)&& (makerBid.price == takerAsk.price) && (makerBid.endTime >= block.timestamp) && (makerBid.startTime <= block.timestamp)), takerAsk.tokenId,//这里体现出任意,即Set持有者选一个给maker makerBid.amount ); }StrategyAnyItemFromCollectionForFixedPrice(固定架构购买指定Collection中任意item)
{ return ( ((makerBid.price == takerAsk.price) && (makerBid.endTime >= block.timestamp) && (makerBid.startTime <= block.timestamp)), takerAsk.tokenId,//这里体现出任意,即Collection持有者选一个给maker makerBid.amount ); }
与前面不同的是,这里的gas费是由NFT所有人支付。
strategy:申购者选定的策略
currency:申购者选定的货币类型
amount:卖家确定的售价
1)协议费结算
与【2.1.6】中的协议费结算类似。
协议费计算公式依然是,
协议费 = 交易金额 * 协议费率
所以,不管交易是由买家还是卖家推动,LooksRare交易所的协议费收费方式都一致。
2)版权费结算
与【2.1.6】中的协议费结算类似。
版权费计算公式依然是,
版权费 = 交易金额 * 版权费率
所以,不管交易是由买家还是卖家推动,Collection版权费收费方式都一致。
3)卖家结算
与【2.1.6】中的结算方式一致。
卖家收款金额 = 交易金额 - 协议费 - 版权费
当然货币类似是taker指定的货币类型。
LooksRare的代码框架还是非常整洁,代码逻辑清晰,分工职责合理。同样这套架构框架,稍微改改即可变成「NFT租赁平台」。
LooksRare最大的吸引点是「挂单奖励」,与租赁市场相结合后会擦出怎样的火花我们拭目以待。
https://docs.looksrare.org/developers/looksrare-exchange-overview

