美文网首页
opensea 合约代码

opensea 合约代码

作者: YANG_ad29 | 来源:发表于2022-05-22 16:49 被阅读0次

    代码库 https://github.com/ProjectWyvern/wyvern-ethereum(不在opensea的仓库下,所以之前没有找到)
    如果要在opensea卖nft 首先需要初始化钱包那什么是初始化钱包呢?

    初始化钱包我理解就是帮你针对opensea 创建一个合约账户

    调用 ProxyRegistry 合约的 registerProxy()方法就创建了,ProxyRegistry是使用opensea用户的合约账户的管理合约

        function registerProxy()
            public
            returns (OwnableDelegateProxy proxy)
        {
            require(proxies[msg.sender] == address(0));
          // new 一个钱包。这个钱包合约是个可升级的代理合约,
            proxy = new OwnableDelegateProxy(msg.sender, delegateProxyImplementation, abi.encodeWithSignature("initialize(address,address)", msg.sender, address(this)));
          //保存到mapping  以 所有的的地址作为索引的key
            proxies[msg.sender] = proxy;
            return proxy;
        }
    

    合约账户的初始化

    contract OwnableDelegateProxy is OwnedUpgradeabilityProxy {
    
        constructor(address owner, address initialImplementation, bytes calldata)
            public
        {
            setUpgradeabilityOwner(owner);
            _upgradeTo(initialImplementation);
            require(initialImplementation.delegatecall(calldata));
        }
    
    }
    

    OwnableDelegateProxy合约账户的代理合约(存储) 它继承至OwnedUpgradeabilityProxy -> Proxy, OwnedUpgradeabilityStorage,这个合约主要是代理合约的逻辑升级,和代理调用的逻辑 ,用户是可以修改这个合约的实现的
    AuthenticatedProxy 这个合约是合约账户的逻辑合约 也就是OwnableDelegateProxy的逻辑功能。他继承了TokenRecipient,OwnedUpgradeabilityStorage。这个合约主要有三个方法
    1 初始化

       //这个方法是 new OwnableDelegateProxy 时调用的设置了所属用户  和管理合约 这两个值时可以修改的 1是在proxy 方法delegatecall修改,还有就是升级逻辑合约
        function initialize (address addrUser, ProxyRegistry addrRegistry)
            public
        {
            require(!initialized);
            initialized = true;
            user = addrUser;
            registry = addrRegistry;
        }
    

    2取消opensea的代理权限

        function setRevoke(bool revoke)
            public
        {
            require(msg.sender == user);
            revoked = revoke;
            emit Revoked(revoke);
        
    

    3 代理

        function proxy(address dest, HowToCall howToCall, bytes calldata)
            public
            returns (bool result)
        {
            require(msg.sender == user || (!revoked && registry.contracts(msg.sender))); //setRevoke  后就只有用户自己可以调用
            if (howToCall == HowToCall.Call) {
                result = dest.call(calldata);
            } else if (howToCall == HowToCall.DelegateCall) {
                result = dest.delegatecall(calldata);
            }
            return result;
        }
    

    4

        function proxyAssert(address dest, HowToCall howToCall, bytes calldata)
            public
        {
            require(proxy(dest, howToCall, calldata));
        }
    

    TokenTransferProxy -> 专门转账的合约

    contract TokenTransferProxy {
    
        /* Authentication registry. */
        ProxyRegistry public registry;
    
        /**
         * Call ERC20 `transferFrom`
         *
         * @dev Authenticated contract only
         * @param token ERC20 token address
         * @param from From address
         * @param to To address
         * @param amount Transfer amount
         */
        function transferFrom(address token, address from, address to, uint amount)
            public
            returns (bool)
        {
            require(registry.contracts(msg.sender));
            return ERC20(token).transferFrom(from, to, amount); 
        }
    
    }
    

    opensea的具体交易功能(以v2为列)
    WyvernExchangeWithBulkCancellations -> Exchange ->ExchangeCore

    WyvernExchangeWithBulkCancellations 就一个构造方法

        constructor (ProxyRegistry registryAddress, TokenTransferProxy tokenTransferProxyAddress, ERC20 tokenAddress, address protocolFeeAddress) public {
            registry = registryAddress; //合约账户的管理合约
            tokenTransferProxy = tokenTransferProxyAddress; //负责转账的
            exchangeToken = tokenAddress; // The token used to pay exchange fees 交手续费的地址
            protocolFeeRecipient = protocolFeeAddress; //接受手续费的地址  opensea的版税是有这个地址先同意收取,再分合集分发的
            owner = msg.sender;
        }
    

    Exchange 主要是对 ExchangeCore的内部部调用。主要逻实现在ExchangeCore 两个方法除外 这三个也是给链下提供查询继续
    1

        function guardedArrayReplace(bytes array, bytes desired, bytes mask)
            public
            pure
            returns (bytes)
        {
            ArrayUtils.guardedArrayReplace(array, desired, mask);
            return array;
        }
    

    2,

    计算价格  有两种交易类型 1.定价,2-卖家价格随时间减少  买-价格随时间增加
     function calculateFinalPrice(SaleKindInterface.Side side, SaleKindInterface.SaleKind saleKind, uint basePrice, uint extra, uint listingTime, uint expirationTime)
            public
            view
            returns (uint)
        {
            return SaleKindInterface.calculateFinalPrice(side, saleKind, basePrice, extra, listingTime, expirationTime);
        }
    
      function calculateFinalPrice(Side side, SaleKind saleKind, uint basePrice, uint extra, uint listingTime, uint expirationTime)
            view
            internal
            returns (uint finalPrice)
        {
            if (saleKind == SaleKind.FixedPrice) {
                return basePrice;
            } else if (saleKind == SaleKind.DutchAuction) {//extra 价格变化的乘数 也就是速率
                uint diff = SafeMath.div(SafeMath.mul(extra, SafeMath.sub(now, listingTime)), SafeMath.sub(expirationTime, listingTime));
                if (side == Side.Sell) {
                    /* Sell-side - start price: basePrice. End price: basePrice - extra. */
                    return SafeMath.sub(basePrice, diff);
                } else {
                    /* Buy-side - start price: basePrice. End price: basePrice + extra. */
                    return SafeMath.add(basePrice, diff);
                }
            }
        }
    

    3

     //两个订单是否匹配 这部分不是特别理解
        function orderCalldataCanMatch(bytes buyCalldata, bytes buyReplacementPattern, bytes sellCalldata, bytes sellReplacementPattern)
            public
            pure
            returns (bool)
        {
            if (buyReplacementPattern.length > 0) {
              ArrayUtils.guardedArrayReplace(buyCalldata, sellCalldata, buyReplacementPattern);
            }
            if (sellReplacementPattern.length > 0) {
              ArrayUtils.guardedArrayReplace(sellCalldata, buyCalldata, sellReplacementPattern);
            }
            return ArrayUtils.arrayEq(buyCalldata, sellCalldata);
        }
    

    ExchangeCore
    1 构造方法

    
        constructor () public {
       // 主要时eip712的设置
            require(keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") == _EIP_712_DOMAIN_TYPEHASH);
            require(keccak256(bytes(name)) == _NAME_HASH);
            require(keccak256(bytes(version)) == _VERSION_HASH);
            require(keccak256("Order(address exchange,address maker,address taker,uint256 makerRelayerFee,uint256 takerRelayerFee,uint256 makerProtocolFee,uint256 takerProtocolFee,address feeRecipient,uint8 feeMethod,uint8 side,uint8 saleKind,address target,uint8 howToCall,bytes calldata,bytes replacementPattern,address staticTarget,bytes staticExtradata,address paymentToken,uint256 basePrice,uint256 extra,uint256 listingTime,uint256 expirationTime,uint256 salt,uint256 nonce)") == _ORDER_TYPEHASH);
            require(DOMAIN_SEPARATOR == _deriveDomainSeparator()); //自己部署时要修改DOMAIN_SEPARATOR
        }
    

    2.approvedOrders //手动approve 后 不需要签名sig就能交易

        function approveOrder(Order memory order, bool orderbookInclusionDesired)
            internal
        {
            /* CHECKS */
    
            /* Assert sender is authorized to approve order. */
            require(msg.sender == order.maker); //谁挂的单
    
            /* Calculate order hash. */
            bytes32 hash = hashToSign(order, nonces[order.maker]); //签名的hash
    
            /* Assert order has not already been approved. */
            require(_approvedOrdersByNonce[hash] == 0);
    
            /* EFFECTS */
    
            /* Mark order as approved. */
            _approvedOrdersByNonce[hash] = nonces[order.maker] + 1;
    
            /* Log approval event. Must be split in two due to Solidity stack size limitations. */
            {
                emit OrderApprovedPartOne(hash, order.exchange, order.maker, order.taker, order.makerRelayerFee, order.takerRelayerFee, order.makerProtocolFee, order.takerProtocolFee, order.feeRecipient, order.feeMethod, order.side, order.saleKind, order.target);
            }
            {
                emit OrderApprovedPartTwo(hash, order.howToCall, order.calldata, order.replacementPattern, order.staticTarget, order.staticExtradata, order.paymentToken, order.basePrice, order.extra, order.listingTime, order.expirationTime, order.salt, orderbookInclusionDesired);
            }
        }
    

    hashToSign

      function hashToSign(Order memory order, uint nonce)
            internal
            pure
            returns (bytes32)
        {
            return keccak256(
                abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashOrder(order, nonce))
            );
        }
    
      function hashOrder(Order memory order, uint nonce)
            internal
            pure
            returns (bytes32 hash)
        {
            /* Unfortunately abi.encodePacked doesn't work here, stack size constraints. */
            uint size = 800;
            bytes memory array = new bytes(size);
            uint index;
            assembly {
                index := add(array, 0x20)
            }
            index = ArrayUtils.unsafeWriteBytes32(index, _ORDER_TYPEHASH);
            index = ArrayUtils.unsafeWriteAddressWord(index, order.exchange);
            index = ArrayUtils.unsafeWriteAddressWord(index, order.maker);
            index = ArrayUtils.unsafeWriteAddressWord(index, order.taker);
            index = ArrayUtils.unsafeWriteUint(index, order.makerRelayerFee);
            index = ArrayUtils.unsafeWriteUint(index, order.takerRelayerFee);
            index = ArrayUtils.unsafeWriteUint(index, order.makerProtocolFee);
            index = ArrayUtils.unsafeWriteUint(index, order.takerProtocolFee);
            index = ArrayUtils.unsafeWriteAddressWord(index, order.feeRecipient);
            index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.feeMethod));
            index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.side));
            index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.saleKind));
            index = ArrayUtils.unsafeWriteAddressWord(index, order.target);
            index = ArrayUtils.unsafeWriteUint8Word(index, uint8(order.howToCall));
            index = ArrayUtils.unsafeWriteBytes32(index, keccak256(order.calldata));
            index = ArrayUtils.unsafeWriteBytes32(index, keccak256(order.replacementPattern));//类型为bytes的进行的hash 与 metamask - Sign Typed Data v4 对应
            index = ArrayUtils.unsafeWriteAddressWord(index, order.staticTarget);
            index = ArrayUtils.unsafeWriteBytes32(index, keccak256(order.staticExtradata));
            index = ArrayUtils.unsafeWriteAddressWord(index, order.paymentToken);
            index = ArrayUtils.unsafeWriteUint(index, order.basePrice);
            index = ArrayUtils.unsafeWriteUint(index, order.extra);
            index = ArrayUtils.unsafeWriteUint(index, order.listingTime);
            index = ArrayUtils.unsafeWriteUint(index, order.expirationTime);
            index = ArrayUtils.unsafeWriteUint(index, order.salt);
            index = ArrayUtils.unsafeWriteUint(index, nonce);
            //这部分应该等同于abi.encode()
            assembly {
                hash := keccak256(add(array, 0x20), size)
            }
            return hash;
        }
    

    取消订单

        function cancelOrder(Order memory order, Sig memory sig, uint nonce)
            internal
        {
            /* CHECKS */
    
            /* Calculate order hash. */
            bytes32 hash = requireValidOrder(order, sig, nonce); //验证签名 - 我认为取消是没有必要验证签名 只要验证参数就行
    
            /* Assert sender is authorized to cancel order. */
            require(msg.sender == order.maker); //只有自己才能取消自己的挂单
    
            /* EFFECTS */
    
            /* Mark order as cancelled, preventing it from being matched. */
            cancelledOrFinalized[hash] = true;//把订单设置为取消或者以交易
    
            /* Log cancel event. */
            emit OrderCancelled(hash);
        }
    
           function requireValidOrder(Order memory order, Sig memory sig, uint nonce)
            internal
            view
            returns (bytes32)
        {
            bytes32 hash = hashToSign(order, nonce); //获取签名的hash
            require(validateOrder(hash, order, sig)); 
            return hash;
        }
    
        function validateOrder(bytes32 hash, Order memory order, Sig memory sig)
            internal
            view
            returns (bool)
        {
            /* Not done in an if-conditional to prevent unnecessary ecrecover evaluation, which seems to happen even though it should short-circuit. */
    
            /* Order must have valid parameters. */
            if (!validateOrderParameters(order)) {//参数验证
                return false;
            }
    
            /* Order must have not been canceled or already filled. */
            if (cancelledOrFinalized[hash]) { //订单是否已经取消或者成交
                return false;
            }
    
            /* Return true if order has been previously approved with the current nonce */
            uint approvedOrderNoncePlusOne = _approvedOrdersByNonce[hash];
            if (approvedOrderNoncePlusOne != 0) {  //订单之前已经手动approve就直接返回true  不需要再验证签名
                return approvedOrderNoncePlusOne == nonces[order.maker] + 1;
            }
    
            //验证签名
            /* Prevent signature malleability and non-standard v values. */
            if (uint256(sig.s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return false;
            }
            if (sig.v != 27 && sig.v != 28) {
                return false;
            }
    
            /* recover via ECDSA, signed by maker (already verified as non-zero). */
            if (ecrecover(hash, sig.v, sig.r, sig.s) == order.maker) {
                return true;
            }
    
           // 如果是合约
            /* fallback — attempt EIP-1271 isValidSignature check. */
            return _tryContractSignature(order.maker, hash, sig);
        }
    
       //验证参数
        function validateOrderParameters(Order memory order)
            internal
            view
            returns (bool)
        {
            /* Order must be targeted at this protocol version (this Exchange contract). */
            if (order.exchange != address(this)) {
                return false;
            }
    
            /* Order must have a maker. */
            if (order.maker == address(0)) {
                return false;
            }
    
            /* Order must possess valid sale kind parameter combination. */
            if (!SaleKindInterface.validateParameters(order.saleKind, order.expirationTime)) {
                return false;
            }
    
            /* If using the split fee method, order must have sufficient protocol fees. */
            if (order.feeMethod == FeeMethod.SplitFee && (order.makerProtocolFee < minimumMakerProtocolFee || order.takerProtocolFee < minimumTakerProtocolFee)) {
                return false;
            }
    
            return true;
        }
    

    atomicMatch 交易

        function atomicMatch(Order memory buy, Sig memory buySig, Order memory sell, Sig memory sellSig, bytes32 metadata)
            internal
            reentrancyGuard
        {
            /* CHECKS */
    
            /* Ensure buy order validity and calculate hash if necessary. */
            bytes32 buyHash;
            if (buy.maker == msg.sender) {  //如果maker是合约调用者就不验证签名
                require(validateOrderParameters(buy));
            } else {
                buyHash = _requireValidOrderWithNonce(buy, buySig);
            }
    
            /* Ensure sell order validity and calculate hash if necessary. */
            bytes32 sellHash;
            if (sell.maker == msg.sender) {
                require(validateOrderParameters(sell));
            } else {
                sellHash = _requireValidOrderWithNonce(sell, sellSig);
            }
    
            /* Must be matchable. */
            require(ordersCanMatch(buy, sell));//订单是否匹配
    
            /* Target must exist (prevent malicious selfdestructs just prior to order settlement). */
            uint size;
            address target = sell.target;
            assembly {
                size := extcodesize(target)
            }
            require(size > 0);
    
            /* Must match calldata after replacement, if specified. */
            if (buy.replacementPattern.length > 0) {
              ArrayUtils.guardedArrayReplace(buy.calldata, sell.calldata, buy.replacementPattern);
            }
            if (sell.replacementPattern.length > 0) {
              ArrayUtils.guardedArrayReplace(sell.calldata, buy.calldata, sell.replacementPattern);
            }
            require(ArrayUtils.arrayEq(buy.calldata, sell.calldata)); //买方要买的 与卖方要卖的是同样的东西。
    
            /* Retrieve delegateProxy contract. */
            OwnableDelegateProxy delegateProxy = registry.proxies(sell.maker); //获取卖方的合约账户
    
            /* Proxy must exist. */
            require(delegateProxy != address(0)); //只有初始钱包才能卖
    
            /* Access the passthrough AuthenticatedProxy. */
            AuthenticatedProxy proxy = AuthenticatedProxy(delegateProxy); //逻辑合约实例化
    
            /* EFFECTS */
    
            /* Mark previously signed or approved orders as finalized. */
            if (msg.sender != buy.maker) { 
                cancelledOrFinalized[buyHash] = true;
            }
            if (msg.sender != sell.maker) {
                cancelledOrFinalized[sellHash] = true;
            }
    
            /* INTERACTIONS */
    
            /* Execute funds transfer and pay fees. */
            uint price = executeFundsTransfer(buy, sell); //交钱,
    
            /* Assert implementation. */
            require(delegateProxy.implementation() == registry.delegateProxyImplementation()); //用户没有修改合约账户的实现
    
            /* Execute specified call through proxy. */  //用户挂单卖货是nft只是授权给自己的合约账户  这也是很多nft合约再检查授权的时候把这个 地址也做了放行  就是说再opensea挂单卖的时候不需要额外授权
            require(proxy.proxy(sell.target, sell.howToCall, sell.calldata)); //卖货  这里是通过用户的合约账户转的nft,由于有时候需要批量转  所以这里就还涉及一个批量call的合约 
      
            /* Static calls are intentionally done after the effectful call so they can check resulting state. */
    
            /* Handle buy-side static call if specified. */
            if (buy.staticTarget != address(0)) {
                require(staticCall(buy.staticTarget, sell.calldata, buy.staticExtradata)); //这里可以做收货校验
            }
    
            /* Handle sell-side static call if specified. */
            if (sell.staticTarget != address(0)) {
                require(staticCall(sell.staticTarget, sell.calldata, sell.staticExtradata)); //这里可以做收钱校验
            }
    
            /* Log match event. */
            emit OrdersMatched(buyHash, sellHash, sell.feeRecipient != address(0) ? sell.maker : buy.maker, sell.feeRecipient != address(0) ? buy.maker : sell.maker, price, metadata);
        }
    

    检查订单是否匹配

    
        function ordersCanMatch(Order memory buy, Order memory sell)
            internal
            view
            returns (bool)
        {
            return (
                /* Must be opposite-side. */
                (buy.side == SaleKindInterface.Side.Buy && sell.side == SaleKindInterface.Side.Sell) &&///买卖方
                /* Must use same fee method. */
                (buy.feeMethod == sell.feeMethod) && //手续费类型一致
                /* Must use same payment token. */
                (buy.paymentToken == sell.paymentToken) &&  //支付方式匹配
                /* Must match maker/taker addresses. */
                (sell.taker == address(0) || sell.taker == buy.maker) &&//是否符合买(卖)对卖(买)方的需求
                (buy.taker == address(0) || buy.taker == sell.maker) &&
                /* One must be maker and the other must be taker (no bool XOR in Solidity). */
                ((sell.feeRecipient == address(0) && buy.feeRecipient != address(0)) || (sell.feeRecipient != address(0) && buy.feeRecipient == address(0))) &&//两方必须有一方且只有一方设置了feeRecipient 地址
                /* Must match target. */
                (buy.target == sell.target) && //
                /* Must match howToCall. */
                (buy.howToCall == sell.howToCall) && //
                /* Buy-side order must be settleable. */
                SaleKindInterface.canSettleOrder(buy.listingTime, buy.expirationTime) &&
                /* Sell-side order must be settleable. */
                SaleKindInterface.canSettleOrder(sell.listingTime, sell.expirationTime)
            );
        }
    
    

    转账

      function executeFundsTransfer(Order memory buy, Order memory sell)
            internal
            returns (uint)
        {
            /* Only payable in the special case of unwrapped Ether. */
            if (sell.paymentToken != address(0)) {//address(0) 代表eth
                require(msg.value == 0);
            }
    
            /* Calculate match price. */ 
            uint price = calculateMatchPrice(buy, sell); //计算成交价格
    
            /* If paying using a token (not Ether), transfer tokens. This is done prior to fee payments to that a seller will have tokens before being charged fees. */
            if (price > 0 && sell.paymentToken != address(0)) {
                transferTokens(sell.paymentToken, buy.maker, sell.maker, price); //付钱给卖家
            }
    
            /* Amount that will be received by seller (for Ether). */
            uint receiveAmount = price;
    
            /* Amount that must be sent by buyer (for Ether). */
            uint requiredAmount = price;
    
            /* Determine maker/taker and charge fees accordingly. */
            if (sell.feeRecipient != address(0)) {  
                /* Sell-side order is maker. */  //只有执行操作的是买家 才能实现eth付款
    
                /* Assert taker fee is less than or equal to maximum fee specified by buyer. */
                require(sell.takerRelayerFee <= buy.takerRelayerFee);
    
                if (sell.feeMethod == FeeMethod.SplitFee) {
                    /* Assert taker fee is less than or equal to maximum fee specified by buyer. */
                    require(sell.takerProtocolFee <= buy.takerProtocolFee);
    
                    /* Maker fees are deducted from the token amount that the maker receives. Taker fees are extra tokens that must be paid by the taker. */
    
                    if (sell.makerRelayerFee > 0) {
                        uint makerRelayerFee = SafeMath.div(SafeMath.mul(sell.makerRelayerFee, price), INVERSE_BASIS_POINT);
                        if (sell.paymentToken == address(0)) {
                            receiveAmount = SafeMath.sub(receiveAmount, makerRelayerFee);
                            sell.feeRecipient.transfer(makerRelayerFee);
                        } else {
                            transferTokens(sell.paymentToken, sell.maker, sell.feeRecipient, makerRelayerFee);
                        }
                    }
    
                    if (sell.takerRelayerFee > 0) {
                        uint takerRelayerFee = SafeMath.div(SafeMath.mul(sell.takerRelayerFee, price), INVERSE_BASIS_POINT);
                        if (sell.paymentToken == address(0)) {
                            requiredAmount = SafeMath.add(requiredAmount, takerRelayerFee);
                            sell.feeRecipient.transfer(takerRelayerFee);
                        } else {
                            transferTokens(sell.paymentToken, buy.maker, sell.feeRecipient, takerRelayerFee);
                        }
                    }
    
                    if (sell.makerProtocolFee > 0) {
                        uint makerProtocolFee = SafeMath.div(SafeMath.mul(sell.makerProtocolFee, price), INVERSE_BASIS_POINT);
                        if (sell.paymentToken == address(0)) {
                            receiveAmount = SafeMath.sub(receiveAmount, makerProtocolFee);
                            protocolFeeRecipient.transfer(makerProtocolFee);
                        } else {
                            transferTokens(sell.paymentToken, sell.maker, protocolFeeRecipient, makerProtocolFee);
                        }
                    }
    
                    if (sell.takerProtocolFee > 0) {
                        uint takerProtocolFee = SafeMath.div(SafeMath.mul(sell.takerProtocolFee, price), INVERSE_BASIS_POINT);
                        if (sell.paymentToken == address(0)) {
                            requiredAmount = SafeMath.add(requiredAmount, takerProtocolFee);
                            protocolFeeRecipient.transfer(takerProtocolFee);
                        } else {
                            transferTokens(sell.paymentToken, buy.maker, protocolFeeRecipient, takerProtocolFee);
                        }
                    }
    
                } else {
                    /* Charge maker fee to seller. */
                    chargeProtocolFee(sell.maker, sell.feeRecipient, sell.makerRelayerFee);
    
                    /* Charge taker fee to buyer. */
                    chargeProtocolFee(buy.maker, sell.feeRecipient, sell.takerRelayerFee);
                }
            } else {
                /* Buy-side order is maker. */
    
                /* Assert taker fee is less than or equal to maximum fee specified by seller. */
                require(buy.takerRelayerFee <= sell.takerRelayerFee);
    
                if (sell.feeMethod == FeeMethod.SplitFee) {
                    /* The Exchange does not escrow Ether, so direct Ether can only be used to with sell-side maker / buy-side taker orders. */
                    require(sell.paymentToken != address(0));
    
                    /* Assert taker fee is less than or equal to maximum fee specified by seller. */
                    require(buy.takerProtocolFee <= sell.takerProtocolFee);
    
                    if (buy.makerRelayerFee > 0) {
                        makerRelayerFee = SafeMath.div(SafeMath.mul(buy.makerRelayerFee, price), INVERSE_BASIS_POINT);
                        transferTokens(sell.paymentToken, buy.maker, buy.feeRecipient, makerRelayerFee);
                    }
    
                    if (buy.takerRelayerFee > 0) {
                        takerRelayerFee = SafeMath.div(SafeMath.mul(buy.takerRelayerFee, price), INVERSE_BASIS_POINT);
                        transferTokens(sell.paymentToken, sell.maker, buy.feeRecipient, takerRelayerFee);
                    }
    
                    if (buy.makerProtocolFee > 0) {
                        makerProtocolFee = SafeMath.div(SafeMath.mul(buy.makerProtocolFee, price), INVERSE_BASIS_POINT);
                        transferTokens(sell.paymentToken, buy.maker, protocolFeeRecipient, makerProtocolFee);
                    }
    
                    if (buy.takerProtocolFee > 0) {
                        takerProtocolFee = SafeMath.div(SafeMath.mul(buy.takerProtocolFee, price), INVERSE_BASIS_POINT);
                        transferTokens(sell.paymentToken, sell.maker, protocolFeeRecipient, takerProtocolFee);
                    }
    
                } else {
                    /* Charge maker fee to buyer. */
                    chargeProtocolFee(buy.maker, buy.feeRecipient, buy.makerRelayerFee);
    
                    /* Charge taker fee to seller. */
                    chargeProtocolFee(sell.maker, buy.feeRecipient, buy.takerRelayerFee);
                }
            }
    
            if (sell.paymentToken == address(0)) { //是eth 
                /* Special-case Ether, order must be matched by buyer. */
                require(msg.value >= requiredAmount);
                sell.maker.transfer(receiveAmount); //付钱给卖家
                /* Allow overshoot for variable-price auctions, refund difference. */
                uint diff = SafeMath.sub(msg.value, requiredAmount);
                if (diff > 0) {
                    buy.maker.transfer(diff); //多了退
                }
            }
    
            /* This contract should never hold Ether, however, we cannot assert this, since it is impossible to prevent anyone from sending Ether e.g. with selfdestruct. */
    
            return price;
        }
    
    
        function transferTokens(address token, address from, address to, uint amount)
            internal
        {
            if (amount > 0) {
                require(tokenTransferProxy.transferFrom(token, from, to, amount)); //交易前授权应该是收给转账合约
            }
        }
    

    计算价格

        function calculateMatchPrice(Order memory buy, Order memory sell)
            view
            internal
            returns (uint)
        {
            /* Calculate sell price. */
            uint sellPrice = SaleKindInterface.calculateFinalPrice(sell.side, sell.saleKind, sell.basePrice, sell.extra, sell.listingTime, sell.expirationTime);
    
            /* Calculate buy price. */
            uint buyPrice = SaleKindInterface.calculateFinalPrice(buy.side, buy.saleKind, buy.basePrice, buy.extra, buy.listingTime, buy.expirationTime);
    
            /* Require price cross. */
            require(buyPrice >= sellPrice);
    
            /* Maker/taker priority. */
            return sell.feeRecipient != address(0) ? sellPrice : buyPrice;
        }
    

    手续费这一块有点不太明白
    require(proxy.proxy(sell.target, sell.howToCall, sell.calldata))
    单个执行的 有个MerkleValidator 合约 主网地址:0xBAf2127B49fC93CbcA6269FAdE0F7F31dF4c88a7

    /**
     *Submitted for verification at Etherscan.io on 2022-02-02
    */
    
    pragma solidity 0.8.11;
    
    interface IERC721 {
        function safeTransferFrom(address from, address to, uint256 tokenId) external;
        function transferFrom(address from, address to, uint256 tokenId) external;
    }
    
    interface IERC1155 {
        function safeTransferFrom(address from, address to, uint256 tokenId, uint256 amount, bytes calldata data) external;
    }
    
    /// @title MerkleValidator enables matching trait-based and collection-based orders for ERC721 and ERC1155 tokens.
    /// @author 0age
    /// @dev This contract is intended to be called during atomicMatch_ via DELEGATECALL.
    contract MerkleValidator {
        /// @dev InvalidProof is thrown on invalid proofs.
        error InvalidProof();
    
        /// @dev UnnecessaryProof is thrown in cases where a proof is supplied without a valid root to match against (root = 0)
        error UnnecessaryProof();
    
        /// @dev Match an ERC721 order, ensuring that the supplied proof demonstrates inclusion of the tokenId in the associated merkle root.
        /// @param from The account to transfer the ERC721 token from — this token must first be approved on the seller's AuthenticatedProxy contract.
        /// @param to The account to transfer the ERC721 token to.
        /// @param token The ERC721 token to transfer.
        /// @param tokenId The ERC721 tokenId to transfer.
        /// @param root A merkle root derived from each valid tokenId — set to 0 to indicate a collection-level or tokenId-specific order.
        /// @param proof A proof that the supplied tokenId is contained within the associated merkle root. Must be length 0 if root is not set.
        /// @return A boolean indicating a successful match and transfer.
        function matchERC721UsingCriteria(
            address from,
            address to,
            IERC721 token,
            uint256 tokenId,
            bytes32 root,
            bytes32[] calldata proof
        ) external returns (bool) {
            // Proof verification is performed when there's a non-zero root.
            if (root != bytes32(0)) {
                _verifyProof(tokenId, root, proof);
            } else if (proof.length != 0) {
                // A root of zero should never have a proof.
                revert UnnecessaryProof();
            }
    
            // Transfer the token.
            token.transferFrom(from, to, tokenId);
    
            return true;
        }
    
        /// @dev Match an ERC721 order using `safeTransferFrom`, ensuring that the supplied proof demonstrates inclusion of the tokenId in the associated merkle root.
        /// @param from The account to transfer the ERC721 token from — this token must first be approved on the seller's AuthenticatedProxy contract.
        /// @param to The account to transfer the ERC721 token to.
        /// @param token The ERC721 token to transfer.
        /// @param tokenId The ERC721 tokenId to transfer.
        /// @param root A merkle root derived from each valid tokenId — set to 0 to indicate a collection-level or tokenId-specific order.
        /// @param proof A proof that the supplied tokenId is contained within the associated merkle root. Must be length 0 if root is not set.
        /// @return A boolean indicating a successful match and transfer.
        function matchERC721WithSafeTransferUsingCriteria(
            address from,
            address to,
            IERC721 token,
            uint256 tokenId,
            bytes32 root,
            bytes32[] calldata proof
        ) external returns (bool) {
            // Proof verification is performed when there's a non-zero root.
            if (root != bytes32(0)) {
                _verifyProof(tokenId, root, proof);
            } else if (proof.length != 0) {
                // A root of zero should never have a proof.
                revert UnnecessaryProof();
            }
    
            // Transfer the token.
            token.safeTransferFrom(from, to, tokenId);
    
            return true;
        }
    
        /// @dev Match an ERC1155 order, ensuring that the supplied proof demonstrates inclusion of the tokenId in the associated merkle root.
        /// @param from The account to transfer the ERC1155 token from — this token must first be approved on the seller's AuthenticatedProxy contract.
        /// @param to The account to transfer the ERC1155 token to.
        /// @param token The ERC1155 token to transfer.
        /// @param tokenId The ERC1155 tokenId to transfer.
        /// @param amount The amount of ERC1155 tokens with the given tokenId to transfer.
        /// @param root A merkle root derived from each valid tokenId — set to 0 to indicate a collection-level or tokenId-specific order.
        /// @param proof A proof that the supplied tokenId is contained within the associated merkle root. Must be length 0 if root is not set.
        /// @return A boolean indicating a successful match and transfer.
        function matchERC1155UsingCriteria(
            address from,
            address to,
            IERC1155 token,
            uint256 tokenId,
            uint256 amount,
            bytes32 root,
            bytes32[] calldata proof
        ) external returns (bool) {
            // Proof verification is performed when there's a non-zero root.
            if (root != bytes32(0)) {
                _verifyProof(tokenId, root, proof);
            } else if (proof.length != 0) {
                // A root of zero should never have a proof.
                revert UnnecessaryProof();
            }
    
            // Transfer the token.
            token.safeTransferFrom(from, to, tokenId, amount, "");
    
            return true;
        }
    
        /// @dev Ensure that a given tokenId is contained within a supplied merkle root using a supplied proof.
        /// @param leaf The tokenId.
        /// @param root A merkle root derived from each valid tokenId.
        /// @param proof A proof that the supplied tokenId is contained within the associated merkle root.
        function _verifyProof(
            uint256 leaf,
            bytes32 root,
            bytes32[] memory proof
        ) private pure {
            bytes32 computedHash = bytes32(leaf);
            for (uint256 i = 0; i < proof.length; i++) {
                bytes32 proofElement = proof[i];
                if (computedHash <= proofElement) {
                    // Hash(current computed hash + current element of the proof)
                    computedHash = _efficientHash(computedHash, proofElement);
                } else {
                    // Hash(current element of the proof + current computed hash)
                    computedHash = _efficientHash(proofElement, computedHash);
                }
            }
            if (computedHash != root) {
                revert InvalidProof();
            }
        }
    
        /// @dev Efficiently hash two bytes32 elements using memory scratch space.
        /// @param a The first element included in the hash.
        /// @param b The second element included in the hash.
        /// @return value The resultant hash of the two bytes32 elements.
        function _efficientHash(
            bytes32 a,
            bytes32 b
        ) private pure returns (bytes32 value) {
            assembly {
                mstore(0x00, a)
                mstore(0x20, b)
                value := keccak256(0x00, 0x40)
            }
        }
    }
    

    批量执行合约 WyvernAtomicizer 主网地址:0xC99f70bFD82fb7c8f8191fdfbFB735606b15e5c5

    /**
     *Submitted for verification at Etherscan.io on 2018-03-08
    */
    
    pragma solidity ^0.4.13;
    
    library WyvernAtomicizer {
    
        function atomicize (address[] addrs, uint[] values, uint[] calldataLengths, bytes calldatas)
            public
        {
            require(addrs.length == values.length && addrs.length == calldataLengths.length);
    
            uint j = 0;
            for (uint i = 0; i < addrs.length; i++) {
                bytes memory calldata = new bytes(calldataLengths[i]);
                for (uint k = 0; k < calldataLengths[i]; k++) {
                    calldata[k] = calldatas[j];
                    j++;
                }
                require(addrs[i].call.value(values[i])(calldata));
            }
        }
    
    }
    

    opensea token(WyvernToken) 主网地址:0x056017c55aE7AE32d12AeF7C679dF83A85ca75Ff 在构造参数传入的。
    在chargeProtocolFee中用到 当订单的feeMethod 为ProtocolFee时用到
    关于交易费
    当订单的feeMethod 为SplitFee ,收取交易费的地址时订单传进来的feeRecipient 交易费的比率也是参数传进来的 。并且合约没有做收费地址校验和手续费比率校验。那应该就是后台做了校验 。也就是说你的挂单要出现在opensea页面opensea就要收取你的交易费 否则不用。
    opensea的合约代码不多,但是团队并没有给出说明文档。要搞清楚具体细节还是需要花些时间。

    相关文章

      网友评论

          本文标题:opensea 合约代码

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