contract ERC721Tradable is ERC721Full, Ownable, ERC721Mintable {
    
    using Strings for string;

    uint256 public currentTokenId = 0;
    //域名前缀
    string public baseTokenURI = "https://ipfs.infura.io/ipfs/Qmba6eb7kj9ztwxXghvXqszgi9hsYo1thBKGDoDF5C1QJF/";
    //每次生成ID的步长
    uint256 public stepNum = 1;
    //是否可以铸造开关
    bool public canMint = true;

   /**
    * @dev emitted on mint
    * @param _to 开始tokenId
    **/
    event Mint(
       address _to
    );

   /**
    * @dev emitted on mint
    * @param _to  转入地址
    * @param _newTokenId NFT ID
    **/
    event MintByTokenId(
       address _to,
       uint256 _newTokenId
    );

    /**
    * @dev emitted on setConfig
    * @param _baseTokenURI 域名前缀
    * @param _stepNum 步长
    * @param _canMint 是否可以挖矿
    **/
    event SetConfig(
        string _baseTokenURI,
        uint256 _stepNum,
        bool _canMint
    );

    /**
    * @dev emitted on setTokenURI
    * @param _tokenId tokenId
    * @param _tokenURI 相对路径
    **/
    event SetTokenURI(
        uint256 _tokenId, 
        string _tokenURI
    );

    constructor(
        string memory _name,
        string memory _symbol
    ) public 
    ERC721Full(_name, _symbol)
    ERC721Mintable()
    {
        _setBaseURI(baseTokenURI);
    }
    
    /**
     * @dev Mints a token to an address with a tokenURI.
     * @param _to address of the future owner of the token
     */
    function mintTo(address _to) public onlyOwner {
        require(canMint == true,"ERC721: canMint is false");
        uint256 newTokenId = _getNextTokenId();
        //铸造TokenId
        _mint(_to, newTokenId);
        _incrementTokenId();
        emit Mint(_to);
    }

   /**
     * @dev Mints a token to an address with tokenId.
     * @param _to address of the future owner of the token
     * @param _newTokenId token ID
     */
    function mintByTokenId(address _to, uint256 _newTokenId) public onlyMinter {
        require(canMint == true,"ERC721: canMint is false");
        require(!_exists(_newTokenId),"ERC721: This id exists");
        //铸造TokenId
        safeMint(_to, _newTokenId);
        _setTokenURI(_newTokenId,"default.png");
        currentTokenId = _newTokenId;
        emit MintByTokenId(_to, _newTokenId);
    }

   /**
     * @dev Check this id exists.
     * @param _tokenId token ID
     */
    function exists(uint256 _tokenId) public view returns(bool) {
        return _exists(_tokenId);
    }


    /**
     * @dev Gets the list of token IDs of the requested owner.
     * @param userAdd address owning the tokens
     * @return uint256[] List of token IDs owned by the requested address
     */
    function tokensOfOwner(address userAdd) public view returns (uint256[] memory) {
        return _tokensOfOwner(userAdd);
    }

    /**
     * 设置NFT合约的基本参数
     * _baseTokenURI 域名前缀
     * _stepNum 步长
     * _canMint 是否可以铸造
     */
    function setConfig(
        string memory _baseTokenURI,
        uint256 _stepNum, 
        bool _canMint
    ) public onlyOwner {
         baseTokenURI = _baseTokenURI;
         _setBaseURI(_baseTokenURI);
         if (_stepNum > 0) {
            stepNum = _stepNum;    
         }
         canMint = _canMint;
         emit SetConfig(_baseTokenURI, _stepNum, _canMint);
    }

    /**
     * @dev 可以通过tokenId修改tokenURI方法, 此方法直接调用721_setTokenURI方法
     */    
    function setTokenURI(uint256 _tokenId, string memory _tokenURI) public{
        require(ownerOf(_tokenId) == msg.sender, "ERC721: tokenId is not allow msg.sender");
        _setTokenURI(_tokenId, _tokenURI);
        emit SetTokenURI(_tokenId, _tokenURI);
    }


    /**
     * @dev calculates the next token ID based on value of currentTokenId
     * @return uint256 for the next token ID
     */
    function _getNextTokenId() private view returns (uint256) {
        return currentTokenId.add(stepNum);
    }

    /**
     * @dev increments the value of currentTokenId
     */
    function _incrementTokenId() private {
        currentTokenId = currentTokenId + stepNum;
    }


}
/**
 * @title ERC721Tradable
 * ERC721Tradable - ERC721 contract that whitelists a trading address, and has minting functionality.
 */
function setConfig(
        string memory _baseTokenURI,
        uint256 _stepNum, 
        bool _canMint
    ) public onlyOwner {
         baseTokenURI = _baseTokenURI;
         _setBaseURI(_baseTokenURI);
         if (_stepNum > 0) {
            stepNum = _stepNum;    
         }
         canMint = _canMint;
         emit SetConfig(_baseTokenURI, _stepNum, _canMint);
    }
/**
 * 设置NFT合约的基本参数
 * _baseTokenURI 域名前缀
 * _stepNum 步长
 * _canMint 是否可以铸造
 */