美文网首页
以太猫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