// Contract: Token
// Target function: function FINAL_SALE_getInfo( address target ) public view returns (uint256[INFO_MAX] memory)
 "caller is not the owner neither manager" );
        _;
    }

    //--------------------------------------------------------
    // constructor
    //--------------------------------------------------------
    constructor() Ownable() ERC721( TOKEN_NAME, TOKEN_SYMBOL ) {
        transferOwnership( OWNER_ADDRESS );
        _manager = msg.sender;

        //-----------------------
        // mainnet
        //-----------------------
        _baseUriForHidden = "ipfs://QmZkJocmGRFCef9t2q2MGUQ4Z7kNH9KPASwEeD2kx11URw/";

        //=======================
        // WONDERLIST SALE
        //=======================
        _WONDERLIST_SALE_start = 1655110800;    // 2022-06-13 09:00:00(UTC)
        _WONDERLIST_SALE_end = 1655132400;      // 2022-06-13 15:00:00(UTC)
        _WONDERLIST_SALE_arrWhiteList.push( IWhiteList(0x6452EBc37A790d8A5614bB3E30cf544eB5555796) );

        //+++++++++++++++++++++++
        // WONDER KIDS SALE
        //+++++++++++++++++++++++
        _WONDERKIDS_SALE_start = 1655110800;    // 2022-06-13 09:00:00(UTC)
        _WONDERKIDS_SALE_end = 1655132400;      // 2022-06-13 15:00:00(UTC)
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0x582b680e1b1c8aCAA9442E80b12CD0efAf1f3bd1) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0x632ae52C10159fDb8f191F778f1aE9cA2ABFEeb3) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0x7e72b15Cd97F08F7aE7a32C06Cca732eeb08C98c) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0x06Dccb6EE7a900759CE426ef713120508f9C5036) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0xe1bAD705EC6710800B3fe507c66a20cbd895Fe8E) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0x6B9cC7A4c3e7A4eB56f29D3B3556FeB9318D1446) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0x174fF1E0136a1bd578D8e27f6C430963b342F59F) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0xD94e994c032fa5132f070D899703c19E686cFE3a) );
        _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(0x9be8f83f81291aacdB12f37a80A464d7Bfe4B8A1) );

        //***********************
        // PUBLIC SALE
        //***********************
        _PUBLIC_SALE_start = 1655132400;        // 2022-06-13 15:00:00(UTC)
        _PUBLIC_SALE_end = 1655154000;          // 2022-06-13 21:00:00(UTC)
        _PUBLIC_SALE_arrWhiteList.push( IWhiteList(0x2821Dbf2685fCda10C64907637aF56c7f92C4dd6) );
        _PUBLIC_SALE_arrWhiteList.push( IWhiteList(0x55E25eb0DbB1AA8a9b47e44fD27438B827D4A986) );
        _PUBLIC_SALE_arrWhiteList.push( IWhiteList(0x62a2CC3889d65666A669533fE8f92a323FE2c9A8) );
        _PUBLIC_SALE_arrWhiteList.push( IWhiteList(0x904f99D3646660096752e0Bc2fd5458962788F7B) );
        _PUBLIC_SALE_arrWhiteList.push( IWhiteList(0x8336f23513c25c52775DB44624BA5B731C8BAE18) );

        //~~~~~~~~~~~~~~~~~~~~~~~
        // FINAL SALE
        //~~~~~~~~~~~~~~~~~~~~~~~
        _FINAL_SALE_start = 1655154000;         // 2022-06-13 21:00:00
        _FINAL_SALE_end = 0;                    // no period
    }

    //--------------------------------------------------------
    // [public] manager
    //--------------------------------------------------------
    function manager() public view returns (address) {
        return( _manager );
    }

    //--------------------------------------------------------
    // [external/onlyOwner] setManager
    //--------------------------------------------------------
    function setManager( address target ) external onlyOwner {
        _manager = target;
    }

    //--------------------------------------------------------
    // [external] baseUriForHidden
    //--------------------------------------------------------
    function baseUriForHidden() external view returns (string memory) {
        return( _baseUriForHidden );
    }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] setBaseUriForHidden
    //--------------------------------------------------------
    function setBaseUriForHidden( string calldata uri ) external onlyOwnerOrManager {
        _baseUriForHidden = uri;
    }

    //--------------------------------------------------------
    // [external] baseUriForRevealed
    //--------------------------------------------------------
    function baseUriForRevealed() external view returns (string memory) {
        return( _baseUriForRevealed );
    }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] setBaseUriForRevealed
    //--------------------------------------------------------
    function setBaseUriForRevealed( string calldata uri ) external onlyOwnerOrManager {
        _baseUriForRevealed = uri;
    }

    //--------------------------------------------------------
    // [external] totalSupply
    //--------------------------------------------------------
    function totalSupply() external view returns (uint256) {
        return( _totalSupply );
    }

    //--------------------------------------------------------
    // [public/override] tokenURI
    //--------------------------------------------------------
    function tokenURI( uint256 tokenId ) public view override returns (string memory) {
        require( _exists( tokenId ), "nonexistent token" );

        if( bytes(_baseUriForRevealed).length > 0 ){
            return( string( abi.encodePacked( _baseUriForRevealed, Strings.toString( tokenId ) ) ) );            
        }

        return( string( abi.encodePacked( _baseUriForHidden, Strings.toString( tokenId ) ) ) );
    }

    //========================================================
    // [public] getInfo - WONDERLIST SALE
    //========================================================
    function WONDERLIST_SALE_getInfo( address target ) public view returns (uint256[INFO_MAX] memory) {
        uint256[INFO_MAX] memory arrRet;

        if( _WONDERLIST_SALE_isSuspended ){ arrRet[INFO_SALE_SUSPENDED] = 1; }
        arrRet[INFO_SALE_START] = _WONDERLIST_SALE_start;
        arrRet[INFO_SALE_END] = _WONDERLIST_SALE_end;
        arrRet[INFO_SALE_PRICE] = WONDERLIST_SALE_PRICE;
        if( _checkWhitelist( _WONDERLIST_SALE_arrWhiteList, target) ){ arrRet[INFO_SALE_WHITELISTED] = 1; }
        arrRet[INFO_SALE_MAX_MINT] = WONDERLIST_SALE_QUANTITY;
        arrRet[INFO_SALE_USER_MINTED] = _WONDERLIST_SALE_mapMinted[target];

        return( arrRet );
    }

    //========================================================
    // [external/onlyOwnerOrManager] write - WONDERLIST SALE
    //========================================================
    // isSuspended
    function WONDERLIST_SALE_suspend( bool flag ) external onlyOwnerOrManager {
        _WONDERLIST_SALE_isSuspended = flag;
    }

    // start-end
    function WONDERLIST_SALE_setStartEnd( uint256 start, uint256 end ) external onlyOwnerOrManager{
        _WONDERLIST_SALE_start = start;
        _WONDERLIST_SALE_end = end;
    }

    // whitelist
    function WONDERLIST_SALE_setWhiteList( address[] calldata lists ) external onlyOwnerOrManager {
        delete _WONDERLIST_SALE_arrWhiteList;

        for( uint256 i=0; i<lists.length; i++ ){
            require( lists[i] != address(0), "WONDERLIST SALE: invalid list"  );
            _WONDERLIST_SALE_arrWhiteList.push( IWhiteList(lists[i]) );
        }
    }

    //========================================================
    // [external/payable] mint - WONDERLIST SALE
    //========================================================
    function WONDERLIST_SALE_mint( uint256 num ) external payable nonReentrant {
        uint256[INFO_MAX] memory arrInfo = WONDERLIST_SALE_getInfo( msg.sender );

        require( arrInfo[INFO_SALE_SUSPENDED] == 0, "WONDERLIST SALE: suspended" );
        require( arrInfo[INFO_SALE_START] == 0 || arrInfo[INFO_SALE_START] <= block.timestamp, "WONDERLIST SALE: not opend" );
        require( arrInfo[INFO_SALE_END] == 0 || arrInfo[INFO_SALE_END] > block.timestamp, "WONDERLIST SALE: finished" );
        require( arrInfo[INFO_SALE_WHITELISTED] == 1, "WONDERLIST SALE: not whitelisted" );
        require( arrInfo[INFO_SALE_MAX_MINT] >= (arrInfo[INFO_SALE_USER_MINTED]+num), "WONDERLIST SALE: reached the limit" );

        _checkPayment( msg.sender, arrInfo[INFO_SALE_PRICE]*num, msg.value );

        _mintTokens( msg.sender, num );
        _WONDERLIST_SALE_mapMinted[msg.sender] += num;
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // [public] getInfo - WONDERKIDS SALE
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    function WONDERKIDS_SALE_getInfo( address target ) public view returns (uint256[INFO_MAX] memory) {
        uint256[INFO_MAX] memory arrRet;

        if( _WONDERKIDS_SALE_isSuspended ){ arrRet[INFO_SALE_SUSPENDED] = 1; }
        arrRet[INFO_SALE_START] = _WONDERKIDS_SALE_start;
        arrRet[INFO_SALE_END] = _WONDERKIDS_SALE_end;
        arrRet[INFO_SALE_PRICE] = WONDERKIDS_SALE_PRICE;
        if( _checkWhitelist( _WONDERKIDS_SALE_arrWhiteList, target) ){ arrRet[INFO_SALE_WHITELISTED] = 1; }
        arrRet[INFO_SALE_MAX_MINT] = WONDERKIDS_SALE_QUANTITY;
        arrRet[INFO_SALE_USER_MINTED] = _WONDERKIDS_SALE_mapMinted[target];

        return( arrRet );
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // [external/onlyOwnerOrManager] write - WONDERKIDS SALE
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // isSuspended
    function WONDERKIDS_SALE_suspend( bool flag ) external onlyOwnerOrManager {
        _WONDERKIDS_SALE_isSuspended = flag;
    }

    // start-end
    function WONDERKIDS_SALE_setStartEnd( uint256 start, uint256 end ) external onlyOwnerOrManager{
        _WONDERKIDS_SALE_start = start;
        _WONDERKIDS_SALE_end = end;
    }

    // whitelist
    function WONDERKIDS_SALE_setWhiteList( address[] calldata lists ) external onlyOwnerOrManager {
        delete _WONDERKIDS_SALE_arrWhiteList;

        for( uint256 i=0; i<lists.length; i++ ){
            require( lists[i] != address(0), "WONDERKIDS SALE: invalid list"  );
            _WONDERKIDS_SALE_arrWhiteList.push( IWhiteList(lists[i]) );
        }
    }

    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // [external/payable] mint - WONDERKIDS SALE
    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    function WONDERKIDS_SALE_mint( uint256 num ) external payable nonReentrant {
        uint256[INFO_MAX] memory arrInfo = WONDERKIDS_SALE_getInfo( msg.sender );

        require( arrInfo[INFO_SALE_SUSPENDED] == 0, "WONDERKIDS SALE: suspended" );
        require( arrInfo[INFO_SALE_START] == 0 || arrInfo[INFO_SALE_START] <= block.timestamp, "WONDERKIDS SALE: not opend" );
        require( arrInfo[INFO_SALE_END] == 0 || arrInfo[INFO_SALE_END] > block.timestamp, "WONDERKIDS SALE: finished" );
        require( arrInfo[INFO_SALE_WHITELISTED] == 1, "WONDERKIDS SALE: not whitelisted" );
        require( arrInfo[INFO_SALE_MAX_MINT] >= (arrInfo[INFO_SALE_USER_MINTED]+num), "WONDERKIDS SALE: reached the limit" );

        _checkPayment( msg.sender, arrInfo[INFO_SALE_PRICE]*num, msg.value );

        _mintTokens( msg.sender, num );
        _WONDERKIDS_SALE_mapMinted[msg.sender] += num;
    }

    //********************************************************
    // [public] getInfo - PUBLIC SALE
    //********************************************************
    function PUBLIC_SALE_getInfo( address target ) public view returns (uint256[INFO_MAX] memory) {
        uint256[INFO_MAX] memory arrRet;

        if( _PUBLIC_SALE_isSuspended ){ arrRet[INFO_SALE_SUSPENDED] = 1; }
        arrRet[INFO_SALE_START] = _PUBLIC_SALE_start;
        arrRet[INFO_SALE_END] = _PUBLIC_SALE_end;
        arrRet[INFO_SALE_PRICE] = PUBLIC_SALE_PRICE;
        if( _checkWhitelist( _PUBLIC_SALE_arrWhiteList, target) ){ arrRet[INFO_SALE_WHITELISTED] = 1; }
        arrRet[INFO_SALE_MAX_MINT] = PUBLIC_SALE_QUANTITY;
        arrRet[INFO_SALE_USER_MINTED] = _PUBLIC_SALE_mapMinted[target];

        return( arrRet );
    }

    //********************************************************
    // [external/onlyOwnerOrManager] write - PUBLIC SALE
    //********************************************************
    // isSuspended
    function PUBLIC_SALE_suspend( bool flag ) external onlyOwnerOrManager {
        _PUBLIC_SALE_isSuspended = flag;
    }

    // start-end
    function PUBLIC_SALE_setStartEnd( uint256 start, uint256 end ) external onlyOwnerOrManager{
        _PUBLIC_SALE_start = start;
        _PUBLIC_SALE_end = end;
    }

    // whitelist
    function PUBLIC_SALE_setWhiteList( address[] calldata lists ) external onlyOwnerOrManager {
        delete _PUBLIC_SALE_arrWhiteList;

        for( uint256 i=0; i<lists.length; i++ ){
            require( lists[i] != address(0), "PUBLIC SALE: invalid list"  );
            _PUBLIC_SALE_arrWhiteList.push( IWhiteList(lists[i]) );
        }
    }

    //********************************************************
    // [external/payable] mint - PUBLIC SALE
    //********************************************************
    function PUBLIC_SALE_mint( uint256 num ) external payable nonReentrant {
        uint256[INFO_MAX] memory arrInfo = PUBLIC_SALE_getInfo( msg.sender );

        require( arrInfo[INFO_SALE_SUSPENDED] == 0, "PUBLIC SALE: suspended" );
        require( arrInfo[INFO_SALE_START] == 0 || arrInfo[INFO_SALE_START] <= block.timestamp, "PUBLIC SALE: not opend" );
        require( arrInfo[INFO_SALE_END] == 0 || arrInfo[INFO_SALE_END] > block.timestamp, "PUBLIC SALE: finished" );
        require( arrInfo[INFO_SALE_WHITELISTED] == 1, "PUBLIC SALE: not whitelisted" );
        require( arrInfo[INFO_SALE_MAX_MINT] >= (arrInfo[INFO_SALE_USER_MINTED]+num), "PUBLIC SALE: reached the limit" );

        _checkPayment( msg.sender, arrInfo[INFO_SALE_PRICE]*num, msg.value );

        _mintTokens( msg.sender, num );
        _PUBLIC_SALE_mapMinted[msg.sender] += num;
    }