contract HDAOToken is ERC20, Ownable {
    uint256 constant MAX_SUPPLY = 10_000_000_000e18;

    // for DAO.
    uint256 public constant AMOUNT_DAO = MAX_SUPPLY / 100 * 8;
    address public  ADDR_DAO ;

    // for liquidity providers
    uint256 public constant AMOUNT_LP = MAX_SUPPLY / 100 * 2;
    address public  ADDR_LP ;

    // for staking
    uint256 public constant AMOUNT_STAKING = MAX_SUPPLY / 100 * 30;
    address public  ADDR_STAKING ;

    // for TEAM.
    uint256 public constant AMOUNT_TEAM = MAX_SUPPLY / 100 * 10;
    address public  ADDR_TEAM ;

    // for airdrop
    uint256 public constant AMOUNT_AIRDROP = MAX_SUPPLY - (AMOUNT_DAO + AMOUNT_STAKING + AMOUNT_LP + AMOUNT_TEAM);
    address public  ADDR_AIRDROP ;

    using EnumerableSet for EnumerableSet.AddressSet;
    EnumerableSet.AddressSet private _minters;

    EnumerableSet.AddressSet private _blackList;

    event MinerAdded(address indexed user);
    event MinerDeleted(address indexed user);
    event BlacklistAdded(address indexed user);
    event BlacklistDeleted(address indexed user);

    bool public vestStarted = false;
    uint256 public  claimPeriodStarts ; 
    uint256 public  claimPeriodEnds ; 

    constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
    }

    function startVest(address _addressDao, address _addressStake, address _addressTeam, address _addressLP, address _addressAirdrop, uint256 _claimPeriodStarts, uint256 _claimPeriodEnds) public onlyOwner {
        require(!vestStarted, "DaoToken: Vest has already started.");
        vestStarted = true;

        require( block.timestamp < _claimPeriodStarts && block.timestamp < _claimPeriodEnds && _claimPeriodEnds > _claimPeriodStarts, "claim period is invalid");

        ADDR_DAO = _addressDao;
        _mint(ADDR_DAO, AMOUNT_DAO);

        ADDR_STAKING = _addressStake;
        _mint(ADDR_STAKING, AMOUNT_STAKING);

        ADDR_LP = _addressLP;
        _mint(ADDR_LP, AMOUNT_LP);

        ADDR_TEAM = _addressTeam;
        _mint(ADDR_TEAM, AMOUNT_TEAM);

        ADDR_AIRDROP = _addressAirdrop;
        _mint(ADDR_AIRDROP, AMOUNT_AIRDROP);

        claimPeriodStarts = _claimPeriodStarts;
        claimPeriodEnds = _claimPeriodEnds;
    }


    function setClaimPeriod( uint256 _claimPeriodStarts, uint256 _claimPeriodEnds) public onlyOwner{
        require( block.timestamp < _claimPeriodStarts && block.timestamp < _claimPeriodEnds && _claimPeriodEnds > _claimPeriodStarts, "claim period is invalid");

        claimPeriodStarts = _claimPeriodStarts;
        claimPeriodEnds = _claimPeriodEnds;
    }

    function queryClaimPeriod() public view returns(uint256 _claimPeriodStarts, uint256 _claimPeriodEnds){
        _claimPeriodStarts = claimPeriodStarts;
        _claimPeriodEnds = claimPeriodEnds;
    }

    // Extend transfer
    function _transfer(address sender, address recipient, uint256 amount) internal override virtual {
      require(!isBlackList(sender), "sender is the black");
      
      super._transfer(sender, recipient, amount);

    }

    // mint with max supply
    function mint(address _to, uint256 _amount) external onlyMinter returns (bool) {
        _mint(_to, _amount);
        return true;
    }
    function burn(address _from ,uint256 _amount) external onlyMinter  returns (bool) {
        _burn(_from, _amount);
        return true;
    }

    function addMinter(address _addMinter) public onlyOwner returns (bool) {
        require(_addMinter != address(0), "XDTToken: _addMinter is the zero address");

        emit MinerAdded( _addMinter );
        return EnumerableSet.add(_minters, _addMinter);
    }

    function delMinter(address _delMinter) public onlyOwner returns (bool) {
        require(_delMinter != address(0), "XDTToken: _delMinter is the zero address");

        emit MinerDeleted( _delMinter );

        return EnumerableSet.remove(_minters, _delMinter);
    }

    function getMinterLength() public view returns (uint256) {
        return EnumerableSet.length(_minters);
    }

    function isMinter(address account) public view returns (bool) {
        return EnumerableSet.contains(_minters, account);
    }

    function getMinter(uint256 _index) public view  returns (address){
        require(_index <= getMinterLength() - 1, "XDTToken: index out of bounds");
        return EnumerableSet.at(_minters, _index);
    }

    // modifier for mint function
    modifier onlyMinter() {
        require(isMinter(msg.sender) || owner() == msg.sender , "caller is not the minter");
        _;
    }


    //blackList
    function addBlackList(address _blackAddress) public onlyOwner returns (bool) {
        require(_blackAddress != address(0), "XDTToken: _blackAddress is the zero address");

        emit BlacklistAdded( _blackAddress );

        return EnumerableSet.add(_blackList, _blackAddress);
    }

    function delBlackList(address _blackAddress) public onlyOwner returns (bool) {
        require(_blackAddress != address(0), "XDTToken: _blackAddress is the zero address");

        emit BlacklistDeleted( _blackAddress );

        return EnumerableSet.remove(_blackList, _blackAddress);
    }

    function getBlackListLength() public view returns (uint256) {
        return EnumerableSet.length(_blackList);
    }

    function isBlackList(address account) public view returns (bool) {
        return EnumerableSet.contains(_blackList, account);
    }

    function getBlackList(uint256 _index) public view  returns (address){
        require(_index <= getBlackListLength() - 1, "XDTToken: index out of bounds");
        return EnumerableSet.at(_blackList, _index);
    }

    // modifier for not in blacklist function
    modifier onlyNotBlackList() {
        require(!isBlackList(msg.sender), "caller is the black");
        _;
    }

}
function _transfer(address sender, address recipient, uint256 amount) internal override virtual {
  require(!isBlackList(sender), "sender is the black");
      
  super._transfer(sender, recipient, amount);

}

// Extend transfer