美文网首页
LooksRareExchange

LooksRareExchange

作者: YANG_ad29 | 来源:发表于2022-05-19 15:29 被阅读0次

    代码库 https://github.com/LooksRare/contracts-exchange-v1
    contracts-exchange-v1 是LooksRare的链上交易合约其实它所实现的功能和opensea非常类似,但是代码的条理比opensea清晰许多。opensea有初始化钱包功能它没有 不过他又一个策略定制的功能。
    代码结构

    1652943489(1).png
    IExecutionStrategy 策略接口
    interface IExecutionStrategy {
        function canExecuteTakerAsk(OrderTypes.TakerOrder calldata takerAsk, OrderTypes.MakerOrder calldata makerBid)
            external
            view
            returns (
                bool,
                uint256,
                uint256
            );
    
        function canExecuteTakerBid(OrderTypes.TakerOrder calldata takerBid, OrderTypes.MakerOrder calldata makerAsk)
            external
            view
            returns (
                bool,
                uint256,
                uint256
            );
    
        function viewProtocolFee() external view returns (uint256);
    }
    

    executionStrategies 策略目录
    1 -> StrategyAnyItemFromCollectionForFixedPrice 指定合集任意nft的固定价格
    2 -> StrategyAnyItemInASetForFixedPrice 指定的n个nft的任意一个的固定价格
    3 -> StrategyDutchAuction 降价拍卖
    4 -> StrategyPrivateSale 指定买卖方
    5 -> StrategyStandardSaleForFixedPrice 指定nft固定价格
    CurrencyManager -> 支付代币管理器 只有添加白名单的代币可以用来交易
    RoyaltyFeeManager -> 版费管理器
    transferManagers -> nft转移的不同实现
    1 -> TransferManagerERC721
    2 -> TransferManagerERC1155
    3 -> TransferManagerNonCompliantERC721
    TransferSelectorNFT -> nft转移的管理器 可以针对不同的合集采用不同的安全转账实现
    ExecutionManager -> 执行策略管理器 针对上面的策略做白名单增减,校验
    LooksRareExchange -> 交易合约 签名采用的是eip712
    构造函数

     constructor(
            address _currencyManager,
            address _executionManager,
            address _royaltyFeeManager,
            address _WETH,
            address _protocolFeeRecipient
        ) {
            // Calculate the domain separator
            DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                    0xda9101ba92939daf4bb2e18cd5f942363b9297fbc3232c9dd964abb1fb70ed71, // keccak256("LooksRareExchange")
                    0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6, // keccak256(bytes("1")) for versionId = 1
                    block.chainid,
                    address(this)
                )
            );
    
            currencyManager = ICurrencyManager(_currencyManager); //设置 支付代币管理器
            executionManager = IExecutionManager(_executionManager); //设置 执行策略管理器
            royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager); 设置 版费管理器
            WETH = _WETH;
            protocolFeeRecipient = _protocolFeeRecipient; //交易费的接收地址
        }
    

    修改各种管理器

      /**
         * @notice Update currency manager
         * @param _currencyManager new currency manager address
         */
        function updateCurrencyManager(address _currencyManager) external onlyOwner {
            require(_currencyManager != address(0), "Owner: Cannot be null address");
            currencyManager = ICurrencyManager(_currencyManager);
            emit NewCurrencyManager(_currencyManager);
        }
    
        /**
         * @notice Update execution manager
         * @param _executionManager new execution manager address
         */
        function updateExecutionManager(address _executionManager) external onlyOwner {
            require(_executionManager != address(0), "Owner: Cannot be null address");
            executionManager = IExecutionManager(_executionManager);
            emit NewExecutionManager(_executionManager);
        }
    
        /**
         * @notice Update protocol fee and recipient
         * @param _protocolFeeRecipient new recipient for protocol fees
         */
        function updateProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner {
            protocolFeeRecipient = _protocolFeeRecipient;
            emit NewProtocolFeeRecipient(_protocolFeeRecipient);
        }
    
        /**
         * @notice Update royalty fee manager
         * @param _royaltyFeeManager new fee manager address
         */
        function updateRoyaltyFeeManager(address _royaltyFeeManager) external onlyOwner {
            require(_royaltyFeeManager != address(0), "Owner: Cannot be null address");
            royaltyFeeManager = IRoyaltyFeeManager(_royaltyFeeManager);
            emit NewRoyaltyFeeManager(_royaltyFeeManager);
        }
    
        /**
         * @notice Update transfer selector NFT
         * @param _transferSelectorNFT new transfer selector address
         */
        function updateTransferSelectorNFT(address _transferSelectorNFT) external onlyOwner {
            require(_transferSelectorNFT != address(0), "Owner: Cannot be null address");
            transferSelectorNFT = ITransferSelectorNFT(_transferSelectorNFT);
    
            emit NewTransferSelectorNFT(_transferSelectorNFT);
        }
    

    在挂单时指定策略地址

     event TakerAsk(
            bytes32 orderHash, // bid hash of the maker order
            uint256 orderNonce, // user order nonce
            address indexed taker, // sender address for the taker ask order
            address indexed maker, // maker address of the initial bid order
            address indexed strategy, // strategy that defines the execution //策略地址
            address currency, // currency address
            address collection, // collection address
            uint256 tokenId, // tokenId transferred
            uint256 amount, // amount of tokens transferred
            uint256 price // final transacted price
        );
    
        event TakerBid(
            bytes32 orderHash, // ask hash of the maker order
            uint256 orderNonce, // user order nonce
            address indexed taker, // sender address for the taker bid order
            address indexed maker, // maker address of the initial ask order
            address indexed strategy, // strategy that defines the execution //策略地址
            address currency, // currency address
            address collection, // collection address
            uint256 tokenId, // tokenId transferred
            uint256 amount, // amount of tokens transferred
            uint256 price // final transacted price
        );
    

    matchAskWithTakerBidUsingETHAndWETH //挂单

      function matchAskWithTakerBidUsingETHAndWETH(
            OrderTypes.TakerOrder calldata takerBid,
            OrderTypes.MakerOrder calldata makerAsk
        ) external payable override nonReentrant {
            require((makerAsk.isOrderAsk) && (!takerBid.isOrderAsk), "Order: Wrong sides");
            require(makerAsk.currency == WETH, "Order: Currency must be WETH");
            require(msg.sender == takerBid.taker, "Order: Taker must be the sender");
    
            // If not enough ETH to cover the price, use WETH
            if (takerBid.price > msg.value) {
                IERC20(WETH).safeTransferFrom(msg.sender, address(this), (takerBid.price - msg.value));
            } else {
                require(takerBid.price == msg.value, "Order: Msg.value too high");
            }
    
            // Wrap ETH sent to this contract
            IWETH(WETH).deposit{value: msg.value}();
    
            // Check the maker ask order
            bytes32 askHash = makerAsk.hash();
            _validateOrder(makerAsk, askHash); //校验
    
            // Retrieve execution parameters
            (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy) 
                .canExecuteTakerBid(takerBid, makerAsk); //策略校验是否符合
    
            require(isExecutionValid, "Strategy: Execution invalid");
    
            // Update maker ask order status to true (prevents replay)
            _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;  //设置ask的这笔交易已执行
    
            // Execution part 1/2     //支付
            _transferFeesAndFundsWithWETH(
                makerAsk.strategy,
                makerAsk.collection,
                tokenId,
                makerAsk.signer,
                takerBid.price,
                makerAsk.minPercentageToAsk
            );
    
            // Execution part 2/2 
            _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount); //转移nft
    
            emit TakerBid(
                askHash,
                makerAsk.nonce,
                takerBid.taker,
                makerAsk.signer,
                makerAsk.strategy,
                makerAsk.currency,
                makerAsk.collection,
                tokenId,
                amount,
                takerBid.price
            );
        }
    

    matchAskWithTakerBid //挂单

     function matchAskWithTakerBid(OrderTypes.TakerOrder calldata takerBid, OrderTypes.MakerOrder calldata makerAsk)
            external
            override
            nonReentrant
        {
            require((makerAsk.isOrderAsk) && (!takerBid.isOrderAsk), "Order: Wrong sides");
            require(msg.sender == takerBid.taker, "Order: Taker must be the sender");
    
            // Check the maker ask order
            bytes32 askHash = makerAsk.hash();
            _validateOrder(makerAsk, askHash);
    
            (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerAsk.strategy)
                .canExecuteTakerBid(takerBid, makerAsk);
    
            require(isExecutionValid, "Strategy: Execution invalid");
    
            // Update maker ask order status to true (prevents replay)
            _isUserOrderNonceExecutedOrCancelled[makerAsk.signer][makerAsk.nonce] = true;
    
            // Execution part 1/2
            _transferFeesAndFunds(
                makerAsk.strategy,
                makerAsk.collection,
                tokenId,
                makerAsk.currency,
                msg.sender,
                makerAsk.signer,
                takerBid.price,
                makerAsk.minPercentageToAsk
            );
    
            // Execution part 2/2
            _transferNonFungibleToken(makerAsk.collection, makerAsk.signer, takerBid.taker, tokenId, amount);
    
            emit TakerBid(
                askHash,
                makerAsk.nonce,
                takerBid.taker,
                makerAsk.signer,
                makerAsk.strategy,
                makerAsk.currency,
                makerAsk.collection,
                tokenId,
                amount,
                takerBid.price
            );
        }
    

    matchBidWithTakerAsk //询价

     function matchBidWithTakerAsk(OrderTypes.TakerOrder calldata takerAsk, OrderTypes.MakerOrder calldata makerBid)
            external
            override
            nonReentrant
        {
            require((!makerBid.isOrderAsk) && (takerAsk.isOrderAsk), "Order: Wrong sides");
            require(msg.sender == takerAsk.taker, "Order: Taker must be the sender");
    
            // Check the maker bid order
            bytes32 bidHash = makerBid.hash();
            _validateOrder(makerBid, bidHash);
    
            (bool isExecutionValid, uint256 tokenId, uint256 amount) = IExecutionStrategy(makerBid.strategy)
                .canExecuteTakerAsk(takerAsk, makerBid);
    
            require(isExecutionValid, "Strategy: Execution invalid");
    
            // Update maker bid order status to true (prevents replay)
            _isUserOrderNonceExecutedOrCancelled[makerBid.signer][makerBid.nonce] = true;
    
            // Execution part 1/2
            _transferNonFungibleToken(makerBid.collection, msg.sender, makerBid.signer, tokenId, amount);
    
            // Execution part 2/2
            _transferFeesAndFunds(
                makerBid.strategy,
                makerBid.collection,
                tokenId,
                makerBid.currency,
                makerBid.signer,
                takerAsk.taker,
                takerAsk.price,
                takerAsk.minPercentageToAsk
            );
    
            emit TakerAsk(
                bidHash,
                makerBid.nonce,
                takerAsk.taker,
                makerBid.signer,
                makerBid.strategy,
                makerBid.currency,
                makerBid.collection,
                tokenId,
                amount,
                takerAsk.price
            );
        }
    

    付款

     function _transferFeesAndFunds(
            address strategy,
            address collection,
            uint256 tokenId,
            address currency,
            address from,
            address to,
            uint256 amount,
            uint256 minPercentageToAsk
        ) internal {
            // Initialize the final amount that is transferred to seller
            uint256 finalSellerAmount = amount;
    
            // 1. Protocol fee
            {
                uint256 protocolFeeAmount = _calculateProtocolFee(strategy, amount); //计算手续费
    
                // Check if the protocol fee is different than 0 for this strategy
                if ((protocolFeeRecipient != address(0)) && (protocolFeeAmount != 0)) {
                    IERC20(currency).safeTransferFrom(from, protocolFeeRecipient, protocolFeeAmount);
                    finalSellerAmount -= protocolFeeAmount;
                }
            }
    
            // 2. Royalty fee
            {
                (address royaltyFeeRecipient, uint256 royaltyFeeAmount) = royaltyFeeManager
                    .calculateRoyaltyFeeAndGetRecipient(collection, tokenId, amount);
    
                // Check if there is a royalty fee and that it is different to 0
                if ((royaltyFeeRecipient != address(0)) && (royaltyFeeAmount != 0)) {
                    IERC20(currency).safeTransferFrom(from, royaltyFeeRecipient, royaltyFeeAmount);
                    finalSellerAmount -= royaltyFeeAmount;
    
                    emit RoyaltyPayment(collection, tokenId, royaltyFeeRecipient, currency, royaltyFeeAmount);
                }
            }
    
            require((finalSellerAmount * 10000) >= (minPercentageToAsk * amount), "Fees: Higher than expected");
    
            // 3. Transfer final amount (post-fees) to seller
            {
                IERC20(currency).safeTransferFrom(from, to, finalSellerAmount);
            }
        }
    

    转移nft

     function _transferNonFungibleToken(
            address collection,
            address from,
            address to,
            uint256 tokenId,
            uint256 amount
        ) internal {
            // Retrieve the transfer manager address
            address transferManager = transferSelectorNFT.checkTransferManagerForToken(collection); //根据不同的合集选择不同的安全转移合约
    
            // If no transfer manager found, it returns address(0)
            require(transferManager != address(0), "Transfer: No NFT transfer manager available");
    
            // If one is found, transfer the token
            ITransferManagerNFT(transferManager).transferNonFungibleToken(collection, from, to, tokenId, amount);
        }
    

    校验订单

     function _validateOrder(OrderTypes.MakerOrder calldata makerOrder, bytes32 orderHash) internal view {
            // Verify whether order nonce has expired
            require(
                (!_isUserOrderNonceExecutedOrCancelled[makerOrder.signer][makerOrder.nonce]) &&
                    (makerOrder.nonce >= userMinOrderNonce[makerOrder.signer]),
                "Order: Matching order expired"
            ); //是否已经执行或者已经取消或者已经过期
    
            // Verify the signer is not address(0)
            require(makerOrder.signer != address(0), "Order: Invalid signer");
    
            // Verify the amount is not 0
            require(makerOrder.amount > 0, "Order: Amount cannot be 0");
    
            // Verify the validity of the signature
            require(
                SignatureChecker.verify(
                    orderHash,
                    makerOrder.signer,
                    makerOrder.v,
                    makerOrder.r,
                    makerOrder.s,
                    DOMAIN_SEPARATOR
                ),
                "Signature: Invalid"
            ); //检查签名
    
            // Verify whether the currency is whitelisted
            require(currencyManager.isCurrencyWhitelisted(makerOrder.currency), "Currency: Not whitelisted"); 
    
            // Verify whether strategy can be executed
            require(executionManager.isStrategyWhitelisted(makerOrder.strategy), "Strategy: Not whitelisted");
        }
    

    相关文章

      网友评论

          本文标题:LooksRareExchange

          本文链接:https://www.haomeiwen.com/subject/loetprtx.html