github.com/diadata-org/diadata@v1.4.593/config/nftContracts/looksrare/contract.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity ^0.8.0;
     3  
     4  // OpenZeppelin contracts
     5  import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
     6  import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
     7  import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
     8  
     9  // LooksRare interfaces
    10  import {ICurrencyManager} from "./interfaces/ICurrencyManager.sol";
    11  import {IExecutionManager} from "./interfaces/IExecutionManager.sol";
    12  import {IExecutionStrategy} from "./interfaces/IExecutionStrategy.sol";
    13  import {IRoyaltyFeeManager} from "./interfaces/IRoyaltyFeeManager.sol";
    14  import {ILooksRareExchange} from "./interfaces/ILooksRareExchange.sol";
    15  import {ITransferManagerNFT} from "./interfaces/ITransferManagerNFT.sol";
    16  import {ITransferSelectorNFT} from "./interfaces/ITransferSelectorNFT.sol";
    17  import {IWETH} from "./interfaces/IWETH.sol";
    18  
    19  // LooksRare libraries
    20  import {OrderTypes} from "./libraries/OrderTypes.sol";
    21  import {SignatureChecker} from "./libraries/SignatureChecker.sol";
    22  
    23  /**
    24   * @title LooksRareExchange
    25   * @notice It is the core contract of the LooksRare exchange.
    26  LOOKSRARELOOKSRARELOOKSRLOOKSRARELOOKSRARELOOKSRARELOOKSRARELOOKSRLOOKSRARELOOKSRARELOOKSR
    27  LOOKSRARELOOKSRARELOOKSRAR'''''''''''''''''''''''''''''''''''OOKSRLOOKSRARELOOKSRARELOOKSR
    28  LOOKSRARELOOKSRARELOOKS:.                                        .;OOKSRARELOOKSRARELOOKSR
    29  LOOKSRARELOOKSRARELOO,.                                            .,KSRARELOOKSRARELOOKSR
    30  LOOKSRARELOOKSRAREL'                ..',;:LOOKS::;,'..                'RARELOOKSRARELOOKSR
    31  LOOKSRARELOOKSRAR.              .,:LOOKSRARELOOKSRARELO:,.              .RELOOKSRARELOOKSR
    32  LOOKSRARELOOKS:.             .;RARELOOKSRARELOOKSRARELOOKSl;.             .:OOKSRARELOOKSR
    33  LOOKSRARELOO;.            .'OKSRARELOOKSRARELOOKSRARELOOKSRARE'.            .;KSRARELOOKSR
    34  LOOKSRAREL,.            .,LOOKSRARELOOK:;;:"""":;;;lELOOKSRARELO,.            .,RARELOOKSR
    35  LOOKSRAR.             .;okLOOKSRAREx:.              .;OOKSRARELOOK;.             .RELOOKSR
    36  LOOKS:.             .:dOOOLOOKSRARE'      .''''..     .OKSRARELOOKSR:.             .LOOKSR
    37  LOx;.             .cKSRARELOOKSRAR'     'LOOKSRAR'     .KSRARELOOKSRARc..            .OKSR
    38  L;.             .cxOKSRARELOOKSRAR.    .LOOKS.RARE'     ;kRARELOOKSRARExc.             .;R
    39  LO'             .;oOKSRARELOOKSRAl.    .LOOKS.RARE.     :kRARELOOKSRAREo;.             'SR
    40  LOOK;.            .,KSRARELOOKSRAx,     .;LOOKSR;.     .oSRARELOOKSRAo,.            .;OKSR
    41  LOOKSk:.            .'RARELOOKSRARd;.      ....       'oOOOOOOOOOOxc'.            .:LOOKSR
    42  LOOKSRARc.             .:dLOOKSRAREko;.            .,lxOOOOOOOOOd:.             .ARELOOKSR
    43  LOOKSRARELo'             .;oOKSRARELOOxoc;,....,;:ldkOOOOOOOOkd;.             'SRARELOOKSR
    44  LOOKSRARELOOd,.            .,lSRARELOOKSRARELOOKSRARELOOKSRkl,.            .,OKSRARELOOKSR
    45  LOOKSRARELOOKSx;.            ..;oxELOOKSRARELOOKSRARELOkxl:..            .:LOOKSRARELOOKSR
    46  LOOKSRARELOOKSRARc.              .':cOKSRARELOOKSRALOc;'.              .ARELOOKSRARELOOKSR
    47  LOOKSRARELOOKSRARELl'                 ...'',,,,''...                 'SRARELOOKSRARELOOKSR
    48  LOOKSRARELOOKSRARELOOo,.                                          .,OKSRARELOOKSRARELOOKSR
    49  LOOKSRARELOOKSRARELOOKSx;.                                      .;xOOKSRARELOOKSRARELOOKSR
    50  LOOKSRARELOOKSRARELOOKSRLO:.                                  .:SRLOOKSRARELOOKSRARELOOKSR
    51  LOOKSRARELOOKSRARELOOKSRLOOKl.                              .lOKSRLOOKSRARELOOKSRARELOOKSR
    52  LOOKSRARELOOKSRARELOOKSRLOOKSRo'.                        .'oLOOKSRLOOKSRARELOOKSRARELOOKSR
    53  LOOKSRARELOOKSRARELOOKSRLOOKSRARd;.                    .;xRELOOKSRLOOKSRARELOOKSRARELOOKSR
    54  LOOKSRARELOOKSRARELOOKSRLOOKSRARELO:.                .:kRARELOOKSRLOOKSRARELOOKSRARELOOKSR
    55  LOOKSRARELOOKSRARELOOKSRLOOKSRARELOOKl.            .cOKSRARELOOKSRLOOKSRARELOOKSRARELOOKSR
    56  LOOKSRARELOOKSRARELOOKSRLOOKSRARELOOKSRo'        'oLOOKSRARELOOKSRLOOKSRARELOOKSRARELOOKSR
    57  LOOKSRARELOOKSRARELOOKSRLOOKSRARELOOKSRARE,.  .,dRELOOKSRARELOOKSRLOOKSRARELOOKSRARELOOKSR
    58  LOOKSRARELOOKSRARELOOKSRLOOKSRARELOOKSRARELOOKSRARELOOKSRARELOOKSRLOOKSRARELOOKSRARELOOKSR
    59   */
    60  contract LooksRareExchange is ILooksRareExchange, ReentrancyGuard, Ownable {
    61      using SafeERC20 for IERC20;
    62  
    63      using OrderTypes for OrderTypes.MakerOrder;
    64      using OrderTypes for OrderTypes.TakerOrder;
    65  
    66      address public immutable WETH;
    67      bytes32 public immutable DOMAIN_SEPARATOR;
    68  
    69      address public protocolFeeRecipient;
    70  
    71      ICurrencyManager public currencyManager;
    72      IExecutionManager public executionManager;
    73      IRoyaltyFeeManager public royaltyFeeManager;
    74      ITransferSelectorNFT public transferSelectorNFT;
    75  
    76      mapping(address => uint256) public userMinOrderNonce;
    77      mapping(address => mapping(uint256 => bool)) private _isUserOrderNonceExecutedOrCancelled;
    78  
    79      event CancelAllOrders(address indexed user, uint256 newMinNonce);
    80      event CancelMultipleOrders(address indexed user, uint256[] orderNonces);
    81      event NewCurrencyManager(address indexed currencyManager);
    82      event NewExecutionManager(address indexed executionManager);
    83      event NewProtocolFeeRecipient(address indexed protocolFeeRecipient);
    84      event NewRoyaltyFeeManager(address indexed royaltyFeeManager);
    85      event NewTransferSelectorNFT(address indexed transferSelectorNFT);
    86  
    87      event RoyaltyPayment(
    88          address indexed collection,
    89          uint256 indexed tokenId,
    90          address indexed royaltyRecipient,
    91          address currency,
    92          uint256 amount
    93      );
    94  
    95      event TakerAsk(
    96          bytes32 orderHash, // bid hash of the maker order
    97          uint256 orderNonce, // user order nonce
    98          address indexed taker, // sender address for the taker ask order
    99          address indexed maker, // maker address of the initial bid order
   100          address indexed strategy, // strategy that defines the execution
   101          address currency, // currency address
   102          address collection, // collection address
   103          uint256 tokenId, // tokenId transferred
   104          uint256 amount, // amount of tokens transferred
   105          uint256 price // final transacted price
   106      );
   107  
   108      event TakerBid(
   109          bytes32 orderHash, // ask hash of the maker order
   110          uint256 orderNonce, // user order nonce
   111          address indexed taker, // sender address for the taker bid order
   112          address indexed maker, // maker address of the initial ask order
   113          address indexed strategy, // strategy that defines the execution
   114          address currency, // currency address
   115          address collection, // collection address
   116          uint256 tokenId, // tokenId transferred
   117          uint256 amount, // amount of tokens transferred
   118          uint256 price // final transacted price
   119      );
   120  
   121      /**
   122       * @notice Constructor
   123       * @param _currencyManager currency manager address
   124       * @param _executionManager execution manager address
   125       * @param _royaltyFeeManager royalty fee manager address
   126       * @param _WETH wrapped ether address (for other chains, use wrapped native asset)
   127       * @param _protocolFeeRecipient protocol fee recipient
   128       */
   129      constructor(
   130          address _currencyManager,
   131          address _executionManager,
   132          address _royaltyFeeManager,
   133          address _WETH,
   134          address _protocolFeeRecipient
   135      ) {
   136          // Calculate the domain separator
   137          DOMAIN_SEPARATOR = keccak256(
   138              abi.encode(
   139                  0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
   140                  0xda9101ba92939daf4bb2e18cd5f942363b9297fbc3232c9dd964abb1fb70ed71, // keccak256("LooksRareExchange")
   141                  0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6, // keccak256(bytes("1")) for versionId = 1
   142                  block.chainid,
   143                  address(this)
   144              )
   145          );
   146  
   147          currencyManager = ICurrencyManager(_currencyManager);
   148          executionManager = IExecutionManager(_executionManager);
   149          royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager);
   150          WETH = _WETH;
   151          protocolFeeRecipient = _protocolFeeRecipient;
   152      }
   153  
   154      /**
   155       * @notice Cancel all pending orders for a sender
   156       * @param minNonce minimum user nonce
   157       */
   158      function cancelAllOrdersForSender(uint256 minNonce) external {
   159          require(minNonce > userMinOrderNonce[msg.sender], "Cancel: Order nonce lower than current");
   160          require(minNonce < userMinOrderNonce[msg.sender] + 500000, "Cancel: Cannot cancel more orders");
   161          userMinOrderNonce[msg.sender] = minNonce;
   162  
   163          emit CancelAllOrders(msg.sender, minNonce);
   164      }
   165  
   166      /**
   167       * @notice Cancel maker orders
   168       * @param orderNonces array of order nonces
   169       */
   170      function cancelMultipleMakerOrders(uint256[] calldata orderNonces) external {
   171          require(orderNonces.length > 0, "Cancel: Cannot be empty");
   172  
   173          for (uint256 i = 0; i < orderNonces.length; i++) {
   174              require(orderNonces[i] >= userMinOrderNonce[msg.sender], "Cancel: Order nonce lower than current");
   175              _isUserOrderNonceExecutedOrCancelled[msg.sender][orderNonces[i]] = true;
   176          }
   177  
   178          emit CancelMultipleOrders(msg.sender, orderNonces);
   179      }
   180  
   181      /**
   182       * @notice Match ask with a taker bid order using ETH
   183       * @param takerBid taker bid order
   184       * @param makerAsk maker ask order
   185       */
   186      function matchAskWithTakerBidUsingETHAndWETH(
   187          OrderTypes.TakerOrder calldata takerBid,
   188          OrderTypes.MakerOrder calldata makerAsk
   189      ) external payable override nonReentrant {
   190          require((makerAsk.isOrderAsk) && (!takerBid.isOrderAsk), "Order: Wrong sides");
   191          require(makerAsk.currency == WETH, "Order: Currency must be WETH");
   192          require(msg.sender == takerBid.taker, "Order: Taker must be the sender");
   193  
   194          // If not enough ETH to cover the price, use WETH
   195          if (takerBid.price > msg.value) {
   196              IERC20(WETH).safeTransferFrom(msg.sender, address(this), (takerBid.price - msg.value));
   197          } else {
   198              require(takerBid.price == msg.value, "Order: Msg.value too high");
   199          }
   200  
   201          // Wrap ETH sent to this contract
   202          IWETH(WETH).deposit{value: msg.value}();
   203  
   204          // Check the maker ask order
   205          bytes32 askHash = makerAsk.hash();
   206          _validateOrder(makerAsk, askHash);
   207  
   208          // Retrieve execution parameters
   209          (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy)
   210              .canExecuteTakerBid(takerBid, makerAsk);
   211  
   212          require(isExecutionValid, "Strategy: Execution invalid");
   213  
   214          // Update maker ask order status to true (prevents replay)
   215          _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;
   216  
   217          // Execution part 1/2
   218          _transferFeesAndFundsWithWETH(
   219              makerAsk.strategy,
   220              makerAsk.collection,
   221              tokenId,
   222              makerAsk.signer,
   223              takerBid.price,
   224              makerAsk.minPercentageToAsk
   225          );
   226  
   227          // Execution part 2/2
   228          _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);
   229  
   230          emit TakerBid(
   231              askHash,
   232              makerAsk.nonce,
   233              takerBid.taker,
   234              makerAsk.signer,
   235              makerAsk.strategy,
   236              makerAsk.currency,
   237              makerAsk.collection,
   238              tokenId,
   239              amount,
   240              takerBid.price
   241          );
   242      }
   243  
   244      /**
   245       * @notice Match a takerBid with a matchAsk
   246       * @param takerBid taker bid order
   247       * @param makerAsk maker ask order
   248       */
   249      function matchAskWithTakerBid(OrderTypes.TakerOrder calldata takerBid, OrderTypes.MakerOrder calldata makerAsk)
   250          external
   251          override
   252          nonReentrant
   253      {
   254          require((makerAsk.isOrderAsk) && (!takerBid.isOrderAsk), "Order: Wrong sides");
   255          require(msg.sender == takerBid.taker, "Order: Taker must be the sender");
   256  
   257          // Check the maker ask order
   258          bytes32 askHash = makerAsk.hash();
   259          _validateOrder(makerAsk, askHash);
   260  
   261          (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy)
   262              .canExecuteTakerBid(takerBid, makerAsk);
   263  
   264          require(isExecutionValid, "Strategy: Execution invalid");
   265  
   266          // Update maker ask order status to true (prevents replay)
   267          _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;
   268  
   269          // Execution part 1/2
   270          _transferFeesAndFunds(
   271              makerAsk.strategy,
   272              makerAsk.collection,
   273              tokenId,
   274              makerAsk.currency,
   275              msg.sender,
   276              makerAsk.signer,
   277              takerBid.price,
   278              makerAsk.minPercentageToAsk
   279          );
   280  
   281          // Execution part 2/2
   282          _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);
   283  
   284          emit TakerBid(
   285              askHash,
   286              makerAsk.nonce,
   287              takerBid.taker,
   288              makerAsk.signer,
   289              makerAsk.strategy,
   290              makerAsk.currency,
   291              makerAsk.collection,
   292              tokenId,
   293              amount,
   294              takerBid.price
   295          );
   296      }
   297  
   298      /**
   299       * @notice Match a takerAsk with a makerBid
   300       * @param takerAsk taker ask order
   301       * @param makerBid maker bid order
   302       */
   303      function matchBidWithTakerAsk(OrderTypes.TakerOrder calldata takerAsk, OrderTypes.MakerOrder calldata makerBid)
   304          external
   305          override
   306          nonReentrant
   307      {
   308          require((!makerBid.isOrderAsk) && (takerAsk.isOrderAsk), "Order: Wrong sides");
   309          require(msg.sender == takerAsk.taker, "Order: Taker must be the sender");
   310  
   311          // Check the maker bid order
   312          bytes32 bidHash = makerBid.hash();
   313          _validateOrder(makerBid, bidHash);
   314  
   315          (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerBid.strategy)
   316              .canExecuteTakerAsk(takerAsk, makerBid);
   317  
   318          require(isExecutionValid, "Strategy: Execution invalid");
   319  
   320          // Update maker bid order status to true (prevents replay)
   321          _isUserOrderNonceExecutedOrCancelled[makerBid.signer][makerBid.nonce] = true;
   322  
   323          // Execution part 1/2
   324          _transferNonFungibleToken(makerBid.collection, msg.sender, makerBid.signer, tokenId, amount);
   325  
   326          // Execution part 2/2
   327          _transferFeesAndFunds(
   328              makerBid.strategy,
   329              makerBid.collection,
   330              tokenId,
   331              makerBid.currency,
   332              makerBid.signer,
   333              takerAsk.taker,
   334              takerAsk.price,
   335              takerAsk.minPercentageToAsk
   336          );
   337  
   338          emit TakerAsk(
   339              bidHash,
   340              makerBid.nonce,
   341              takerAsk.taker,
   342              makerBid.signer,
   343              makerBid.strategy,
   344              makerBid.currency,
   345              makerBid.collection,
   346              tokenId,
   347              amount,
   348              takerAsk.price
   349          );
   350      }
   351  
   352      /**
   353       * @notice Update currency manager
   354       * @param _currencyManager new currency manager address
   355       */
   356      function updateCurrencyManager(address _currencyManager) external onlyOwner {
   357          require(_currencyManager != address(0), "Owner: Cannot be null address");
   358          currencyManager = ICurrencyManager(_currencyManager);
   359          emit NewCurrencyManager(_currencyManager);
   360      }
   361  
   362      /**
   363       * @notice Update execution manager
   364       * @param _executionManager new execution manager address
   365       */
   366      function updateExecutionManager(address _executionManager) external onlyOwner {
   367          require(_executionManager != address(0), "Owner: Cannot be null address");
   368          executionManager = IExecutionManager(_executionManager);
   369          emit NewExecutionManager(_executionManager);
   370      }
   371  
   372      /**
   373       * @notice Update protocol fee and recipient
   374       * @param _protocolFeeRecipient new recipient for protocol fees
   375       */
   376      function updateProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner {
   377          protocolFeeRecipient = _protocolFeeRecipient;
   378          emit NewProtocolFeeRecipient(_protocolFeeRecipient);
   379      }
   380  
   381      /**
   382       * @notice Update royalty fee manager
   383       * @param _royaltyFeeManager new fee manager address
   384       */
   385      function updateRoyaltyFeeManager(address _royaltyFeeManager) external onlyOwner {
   386          require(_royaltyFeeManager != address(0), "Owner: Cannot be null address");
   387          royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager);
   388          emit NewRoyaltyFeeManager(_royaltyFeeManager);
   389      }
   390  
   391      /**
   392       * @notice Update transfer selector NFT
   393       * @param _transferSelectorNFT new transfer selector address
   394       */
   395      function updateTransferSelectorNFT(address _transferSelectorNFT) external onlyOwner {
   396          require(_transferSelectorNFT != address(0), "Owner: Cannot be null address");
   397          transferSelectorNFT = ITransferSelectorNFT(_transferSelectorNFT);
   398  
   399          emit NewTransferSelectorNFT(_transferSelectorNFT);
   400      }
   401  
   402      /**
   403       * @notice Check whether user order nonce is executed or cancelled
   404       * @param user address of user
   405       * @param orderNonce nonce of the order
   406       */
   407      function isUserOrderNonceExecutedOrCancelled(address user, uint256 orderNonce) external view returns (bool) {
   408          return _isUserOrderNonceExecutedOrCancelled[user][orderNonce];
   409      }
   410  
   411      /**
   412       * @notice Transfer fees and funds to royalty recipient, protocol, and seller
   413       * @param strategy address of the execution strategy
   414       * @param collection non fungible token address for the transfer
   415       * @param tokenId tokenId
   416       * @param currency currency being used for the purchase (e.g., WETH/USDC)
   417       * @param from sender of the funds
   418       * @param to seller's recipient
   419       * @param amount amount being transferred (in currency)
   420       * @param minPercentageToAsk minimum percentage of the gross amount that goes to ask
   421       */
   422      function _transferFeesAndFunds(
   423          address strategy,
   424          address collection,
   425          uint256 tokenId,
   426          address currency,
   427          address from,
   428          address to,
   429          uint256 amount,
   430          uint256 minPercentageToAsk
   431      ) internal {
   432          // Initialize the final amount that is transferred to seller
   433          uint256 finalSellerAmount = amount;
   434  
   435          // 1. Protocol fee
   436          {
   437              uint256 protocolFeeAmount = _calculateProtocolFee(strategy, amount);
   438  
   439              // Check if the protocol fee is different than 0 for this strategy
   440              if ((protocolFeeRecipient != address(0)) && (protocolFeeAmount != 0)) {
   441                  IERC20(currency).safeTransferFrom(from, protocolFeeRecipient, protocolFeeAmount);
   442                  finalSellerAmount -= protocolFeeAmount;
   443              }
   444          }
   445  
   446          // 2. Royalty fee
   447          {
   448              (address royaltyFeeRecipient, uint256 royaltyFeeAmount) = royaltyFeeManager
   449                  .calculateRoyaltyFeeAndGetRecipient(collection, tokenId, amount);
   450  
   451              // Check if there is a royalty fee and that it is different to 0
   452              if ((royaltyFeeRecipient != address(0)) && (royaltyFeeAmount != 0)) {
   453                  IERC20(currency).safeTransferFrom(from, royaltyFeeRecipient, royaltyFeeAmount);
   454                  finalSellerAmount -= royaltyFeeAmount;
   455  
   456                  emit RoyaltyPayment(collection, tokenId, royaltyFeeRecipient, currency, royaltyFeeAmount);
   457              }
   458          }
   459  
   460          require((finalSellerAmount * 10000) >= (minPercentageToAsk * amount), "Fees: Higher than expected");
   461  
   462          // 3. Transfer final amount (post-fees) to seller
   463          {
   464              IERC20(currency).safeTransferFrom(from, to, finalSellerAmount);
   465          }
   466      }
   467  
   468      /**
   469       * @notice Transfer fees and funds to royalty recipient, protocol, and seller
   470       * @param strategy address of the execution strategy
   471       * @param collection non fungible token address for the transfer
   472       * @param tokenId tokenId
   473       * @param to seller's recipient
   474       * @param amount amount being transferred (in currency)
   475       * @param minPercentageToAsk minimum percentage of the gross amount that goes to ask
   476       */
   477      function _transferFeesAndFundsWithWETH(
   478          address strategy,
   479          address collection,
   480          uint256 tokenId,
   481          address to,
   482          uint256 amount,
   483          uint256 minPercentageToAsk
   484      ) internal {
   485          // Initialize the final amount that is transferred to seller
   486          uint256 finalSellerAmount = amount;
   487  
   488          // 1. Protocol fee
   489          {
   490              uint256 protocolFeeAmount = _calculateProtocolFee(strategy, amount);
   491  
   492              // Check if the protocol fee is different than 0 for this strategy
   493              if ((protocolFeeRecipient != address(0)) && (protocolFeeAmount != 0)) {
   494                  IERC20(WETH).safeTransfer(protocolFeeRecipient, protocolFeeAmount);
   495                  finalSellerAmount -= protocolFeeAmount;
   496              }
   497          }
   498  
   499          // 2. Royalty fee
   500          {
   501              (address royaltyFeeRecipient, uint256 royaltyFeeAmount) = royaltyFeeManager
   502                  .calculateRoyaltyFeeAndGetRecipient(collection, tokenId, amount);
   503  
   504              // Check if there is a royalty fee and that it is different to 0
   505              if ((royaltyFeeRecipient != address(0)) && (royaltyFeeAmount != 0)) {
   506                  IERC20(WETH).safeTransfer(royaltyFeeRecipient, royaltyFeeAmount);
   507                  finalSellerAmount -= royaltyFeeAmount;
   508  
   509                  emit RoyaltyPayment(collection, tokenId, royaltyFeeRecipient, address(WETH), royaltyFeeAmount);
   510              }
   511          }
   512  
   513          require((finalSellerAmount * 10000) >= (minPercentageToAsk * amount), "Fees: Higher than expected");
   514  
   515          // 3. Transfer final amount (post-fees) to seller
   516          {
   517              IERC20(WETH).safeTransfer(to, finalSellerAmount);
   518          }
   519      }
   520  
   521      /**
   522       * @notice Transfer NFT
   523       * @param collection address of the token collection
   524       * @param from address of the sender
   525       * @param to address of the recipient
   526       * @param tokenId tokenId
   527       * @param amount amount of tokens (1 for ERC721, 1+ for ERC1155)
   528       * @dev For ERC721, amount is not used
   529       */
   530      function _transferNonFungibleToken(
   531          address collection,
   532          address from,
   533          address to,
   534          uint256 tokenId,
   535          uint256 amount
   536      ) internal {
   537          // Retrieve the transfer manager address
   538          address transferManager = transferSelectorNFT.checkTransferManagerForToken(collection);
   539  
   540          // If no transfer manager found, it returns address(0)
   541          require(transferManager != address(0), "Transfer: No NFT transfer manager available");
   542  
   543          // If one is found, transfer the token
   544          ITransferManagerNFT(transferManager).transferNonFungibleToken(collection, from, to, tokenId, amount);
   545      }
   546  
   547      /**
   548       * @notice Calculate protocol fee for an execution strategy
   549       * @param executionStrategy strategy
   550       * @param amount amount to transfer
   551       */
   552      function _calculateProtocolFee(address executionStrategy, uint256 amount) internal view returns (uint256) {
   553          uint256 protocolFee = IExecutionStrategy(executionStrategy).viewProtocolFee();
   554          return (protocolFee * amount) / 10000;
   555      }
   556  
   557      /**
   558       * @notice Verify the validity of the maker order
   559       * @param makerOrder maker order
   560       * @param orderHash computed hash for the order
   561       */
   562      function _validateOrder(OrderTypes.MakerOrder calldata makerOrder, bytes32 orderHash) internal view {
   563          // Verify whether order nonce has expired
   564          require(
   565              (!_isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.nonce]) &&
   566                  (makerOrder.nonce >= userMinOrderNonce[makerOrder.signer]),
   567              "Order: Matching order expired"
   568          );
   569  
   570          // Verify the signer is not address(0)
   571          require(makerOrder.signer != address(0), "Order: Invalid signer");
   572  
   573          // Verify the amount is not 0
   574          require(makerOrder.amount > 0, "Order: Amount cannot be 0");
   575  
   576          // Verify the validity of the signature
   577          require(
   578              SignatureChecker.verify(
   579                  orderHash,
   580                  makerOrder.signer,
   581                  makerOrder.v,
   582                  makerOrder.r,
   583                  makerOrder.s,
   584                  DOMAIN_SEPARATOR
   585              ),
   586              "Signature: Invalid"
   587          );
   588  
   589          // Verify whether the currency is whitelisted
   590          require(currencyManager.isCurrencyWhitelisted(makerOrder.currency), "Currency: Not whitelisted");
   591  
   592          // Verify whether strategy can be executed
   593          require(executionManager.isStrategyWhitelisted(makerOrder.strategy), "Strategy: Not whitelisted");
   594      }
   595  }