美文网首页
以太猫CryptoKitties源码(四):拍卖

以太猫CryptoKitties源码(四):拍卖

作者: 雪飘千里 | 来源:发表于2019-02-24 17:36 被阅读0次

    8、拍卖

    这个拍卖核心类,与整个迷恋猫项目的关系不大,是相当独立的,其实任何ERC721 代币都可以使用这个拍卖核心类。
    ClockAuctionBase .sol

    /// @title 拍卖核心类 ,任何ERC721 代币都可以使用这个拍卖核心类
    /// @dev 包含了模型、变量、以及拍卖的内部函数
    contract ClockAuctionBase {
    
        // 拍卖的结构体
        struct Auction {
            // 代币的拥有者
            address seller;
            // 起始拍卖价格
            uint128 startingPrice;
            // 限定最低的拍卖价格
            uint128 endingPrice;
            // 拍卖的存续期(拍卖时间,一天,一个月)
            uint64 duration;
            // 拍卖开始时间,startedAt为0时,标识拍卖结束了
            uint64 startedAt;
        }
    
        // ERC721 接口类,
        ERC721 public nonFungibleContract;
    
        // 手续费百分比
        uint256 public ownerCut;
    
        // ERC721代币  对应  拍卖结构体
        mapping (uint256 => Auction) tokenIdToAuction;
    
        //拍卖创建事件
        event AuctionCreated(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration);
        // 拍卖成功事件
        event AuctionSuccessful(uint256 tokenId, uint256 totalPrice, address winner);
       //拍卖移除事件   
     event AuctionCancelled(uint256 tokenId);
    
        /// @dev 判断代币的拥有者是否为_claimant
        /// @param _claimant 拥有者的地址
        /// @param _tokenId  代币ID
        function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
            return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
        }
    
        /// 只要有授权,就可以转移我的代币,暂时托管我的代币
        function _escrow(address _owner, uint256 _tokenId) internal {
            // 相当于先把我的代币转移给了本合约,由本合约先管理我的代币(托管)
            nonFungibleContract.transferFrom(_owner, this, _tokenId);
        }
    
        /// 通过调用ERC721接口中的transfer方法,实现了代币的转移
        function _transfer(address _receiver, uint256 _tokenId) internal {
            // it will throw if transfer fails
            nonFungibleContract.transfer(_receiver, _tokenId);
        }
    
        ///  增加了一个新的拍卖
        /// @param _tokenId  要拍卖的代币的id
        /// @param _auction 拍卖的结构体
        function _addAuction(uint256 _tokenId, Auction _auction) internal {
            // 拍卖的时间必须要大于1分钟
            require(_auction.duration >= 1 minutes);
            // 绑定 代币ID 和 拍卖 
            tokenIdToAuction[_tokenId] = _auction;
            // 触发一个拍卖创建事件
            AuctionCreated(
                uint256(_tokenId),
                uint256(_auction.startingPrice),
                uint256(_auction.endingPrice),
                uint256(_auction.duration)
            );
        }
    
        /// 退出拍卖
        function _cancelAuction(uint256 _tokenId, address _seller) internal {
           //移除拍卖
             _removeAuction(_tokenId);
          // 将代币返回给拍卖者,之前是托管在合约
            _transfer(_seller, _tokenId);
          //触发移除拍卖事件
            AuctionCancelled(_tokenId);
        }
    
        /// 拍卖完成
        /// 我想出_bidAmount金额购买_tokenId代币
        function _bid(uint256 _tokenId, uint256 _bidAmount)
            internal
            returns (uint256)
        {
            // 获取拍卖引用,通过代币_tokenId
            Auction storage auction = tokenIdToAuction[_tokenId];
    
            // 判断是否还在拍卖,如果为0 ,说明拍卖结束了
            require(_isOnAuction(auction));
    
            // 获取当前拍卖价格,拍卖价格是动态的,是随着时间的变化而减少的
            uint256 price = _currentPrice(auction);
           // 我的出价 >  当前的拍卖价格
            require(_bidAmount >= price);
    
            // 获取拍卖的卖家
            address seller = auction.seller;
    
            // 移除拍卖
            _removeAuction(_tokenId);
    
            // 当前拍卖价格 要 大于 0 
            if (price > 0) {
                // 计算手续费
                uint256 auctioneerCut = _computeCut(price);
                // 卖家可以获得的金额 =  最终价格 - 手续费
                uint256 sellerProceeds = price - auctioneerCut;
    
                // 转账到卖家的账户中
                seller.transfer(sellerProceeds);
            }
    
            // 计算多余的金额
            uint256 bidExcess = _bidAmount - price;
    
            // 返还多余的 
            msg.sender.transfer(bidExcess);
    
            // 触发拍卖成功事件
            AuctionSuccessful(_tokenId, price, msg.sender);
    
            return price;
        }
    
        /// 从mapping中移除拍卖
        function _removeAuction(uint256 _tokenId) internal {
            delete tokenIdToAuction[_tokenId];
        }
    
        /// 判断是否还在拍卖,如果startedAt 起始事件 为 0 ,说明已经拍卖结束
        function _isOnAuction(Auction storage _auction) internal view returns (bool) {
            return (_auction.startedAt > 0);
        }
    
        /// 返回当前拍卖价格
        function _currentPrice(Auction storage _auction)
            internal
            view
            returns (uint256)
        {
            uint256 secondsPassed = 0;
    
            // 当前时间要大于拍卖开始时间
            if (now > _auction.startedAt) {
                //计算拍卖开始多少时间了
                secondsPassed = now - _auction.startedAt;
            }
    
            return _computeCurrentPrice(
                _auction.startingPrice,
                _auction.endingPrice,
                _auction.duration,
                secondsPassed
            );
        }
    
        /// 计算当前的拍卖价格
        /// 拍卖的价格是随着时间而逐渐降低的,如果没有人购买的话
        function _computeCurrentPrice(
            uint256 _startingPrice,
            uint256 _endingPrice,
            uint256 _duration,
            uint256 _secondsPassed
        )
            internal
            pure
            returns (uint256)
        {
            // 拍卖已经开始了多少时间  >   拍卖存续期 ,说明拍卖已经结束,返回最终价格
            if (_secondsPassed >= _duration) {
                // 
                return _endingPrice;
            } else {
                //  当前的价格,totalPriceChange 实际上是负数,因为_endingPrice 永远小于 _startingPrice
                int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice);
    
                // 随着时间的流逝,currentPriceChange(负数) 会越来越小,
                // 最小  =  拍卖限定最低价   -  拍卖起始价格
                int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration);
    
                //  当前价格  = 起始价格 + currentPriceChange(负数)
                // 当前价格最低等于 限定最低价
                int256 currentPrice = int256(_startingPrice) + currentPriceChange;
    
                return uint256(currentPrice);
            }
        }
    
        /// 计算手续费
        function _computeCut(uint256 _price) internal view returns (uint256) {
            // ownerCut  为 0 到 10000的数,代表的是万分只几的手续费率
            return _price * ownerCut / 10000;
        }
    
    }
    

    ClockAuction.sol

    /// 
    contract ClockAuction is Pausable, ClockAuctionBase {
    
        /// 接口验证,判断是否ERC721协议
        bytes4 constant InterfaceSignature_ERC721 = bytes4(0x9a20483d);
    
        /// 构造函数
        /// @param _nftAddress - address of a deployed contract implementing
        ///  the Nonfungible Interface.
        /// @param _cut - percent cut the owner takes on each auction, must be
        ///  between 0-10,000.
        function ClockAuction(address _nftAddress, uint256 _cut) public {
            //交易费 判断
            require(_cut <= 10000);
            ownerCut = _cut;
            // 实例化 ERC721 接口,_nftAddress为ERC721协议接口实例的合约地址
            ERC721 candidateContract = ERC721(_nftAddress);
            // 代币是否满足规定的要求,拥有我想要的函数
            require(candidateContract.supportsInterface(InterfaceSignature_ERC721));
            // 赋值给ClockAuctionBase中的nonFungibleContract 
            nonFungibleContract = candidateContract;
        }
    
        /// 转账,将当前的拍卖合约的以太币转移给ERC721合约 
        function withdrawBalance() external {
            // 把接口强转为 地址类型
            address nftAddress = address(nonFungibleContract);
            // 权限判断
            require(
                msg.sender == owner ||
                msg.sender == nftAddress
            );
            // 
            bool res = nftAddress.send(this.balance);
        }
    
        /// @dev 创建一个新的拍卖
        /// @param _tokenId - 代币的id
        /// @param _startingPrice - 开始的价格
        /// @param _endingPrice - 最低的价格
        /// @param _duration - 合约的存取期
        /// @param _seller - 拍卖着
        function createAuction(
            uint256 _tokenId,
            uint256 _startingPrice,
            uint256 _endingPrice,
            uint256 _duration,
            address _seller
        )
            external
            whenNotPaused
        {
            // 限制_startingPrice _endingPrice _duration 都不能超过所指定的范围
            require(_startingPrice == uint256(uint128(_startingPrice)));
            require(_endingPrice == uint256(uint128(_endingPrice)));
            require(_duration == uint256(uint64(_duration)));
            // 合约的调用者 必须 是代币的拥有者
            require(_owns(msg.sender, _tokenId));
            // 托管(合约的调用者的)代币到合约中
            _escrow(msg.sender, _tokenId);
            // 构造拍卖的结构体
            Auction memory auction = Auction(
                _seller,
                uint128(_startingPrice),
                uint128(_endingPrice),
                uint64(_duration),
                uint64(now)
            );
            // 增加了一个新的拍卖
            _addAuction(_tokenId, auction);
        }
    
        /// 完成拍卖
        /// @param _tokenId - 代币的ID
        function bid(uint256 _tokenId)
            external
            payable
            whenNotPaused
        {
            // 处理金额,调用父合约ClockAuctionBase 中的_bid
            _bid(_tokenId, msg.value);
           // 把代币的所有权转移给买家
            _transfer(msg.sender, _tokenId);
        }
    
        /// 退出拍卖,外部函数
        /// @param _tokenId - 代币的ID
        function cancelAuction(uint256 _tokenId)
            external
        {
            // 拍卖的引用
            Auction storage auction = tokenIdToAuction[_tokenId];
            // 判断是否在交易期
            require(_isOnAuction(auction));
            // 获取拍卖人的地址
            address seller = auction.seller;
            // 判断合约的调用者必须是拍卖人
            require(msg.sender == seller);
            // 退出拍卖
            _cancelAuction(_tokenId, seller);
        }
    
        /// 游戏暂停时调用,只有拍卖的拥有者才能调用
        /// 当程序遇到bug时,可以补救
        /// @param _tokenId -  代币的ID
        function cancelAuctionWhenPaused(uint256 _tokenId)
            whenPaused
            onlyOwner
            external
        {
            Auction storage auction = tokenIdToAuction[_tokenId];
            require(_isOnAuction(auction));
            _cancelAuction(_tokenId, auction.seller);
        }
    
        /// 获取拍卖信息,外部,不花费gas的调用,可以再前端查询
        /// @param _tokenId - 代币的 ID
        function getAuction(uint256 _tokenId)
            external
            view
            returns
        (
            address seller,
            uint256 startingPrice,
            uint256 endingPrice,
            uint256 duration,
            uint256 startedAt
        ) {
            Auction storage auction = tokenIdToAuction[_tokenId];
            require(_isOnAuction(auction));
            return (
                auction.seller,
                auction.startingPrice,
                auction.endingPrice,
                auction.duration,
                auction.startedAt
            );
        }
    
        /// 获取代币的当前价格
        /// @param _tokenId - 代币的ID
        function getCurrentPrice(uint256 _tokenId)
            external
            view
            returns (uint256)
        {
            Auction storage auction = tokenIdToAuction[_tokenId];
            // 游戏必须在运行
            require(_isOnAuction(auction));
            return _currentPrice(auction);
        }
    
    }
    

    Pausable.sol

    /**
     * 游戏是否可以暂停
     */
    contract Pausable is Ownable {
      event Pause();
      event Unpause();
    
      bool public paused = false;
    
    
      /**
       * 修饰器 没有暂停
       */
      modifier whenNotPaused() {
        require(!paused);
        _;
      }
    
      /**
       * 修饰器  暂停
       */
      modifier whenPaused {
        require(paused);
        _;
      }
    
      /**
       * @dev called by the owner to pause, triggers stopped state
       */
      function pause() onlyOwner whenNotPaused returns (bool) {
        paused = true;
        Pause();
        return true;
      }
    
      /**
       * @dev called by the owner to unpause, returns to normal state
       */
      function unpause() onlyOwner whenPaused returns (bool) {
        paused = false;
        Unpause();
        return true;
      }
    }
    

    相关文章

      网友评论

          本文标题:以太猫CryptoKitties源码(四):拍卖

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