// File: lib/openzeppelin-contracts/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// File: lib/openzeppelin-contracts/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: src/VestingFactory.sol



// SPDX-License-Identifier: MIT
pragma solidity =0.8.11;
/**
 * @title VestingFactory
 *
 * @notice This contract allows the owner to create, initialize and manage vesting pools with specified percentage
 * shares and names. The total percentage share of all pools cannot exceed 100%.
 *
 * @dev The contract uses OpenZeppelin's Ownable, SafeERC20, and IERC20 libraries.
 */

import "./VestingPool.sol";
import "./interface/IVestingFactory.sol";
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-contracts/contracts/access/Ownable.sol";

contract VestingFactory is Ownable, IVestingFactory {
    /**
     * @dev Mapping to store information about each vesting pool, with the address of the pool contract as the key and a
     * PoolInfo struct as the value.
     */
    mapping(address => PoolInfo) private _pools;
    /**
     * @dev Total percentage of the token held by all vesting pools combined.
     */
    uint256 public poolTotalPercent = 0;
    /**
     * @dev Total token distributed to the vesting pools.
     */
    uint256 public totalDistributed;
    /**
     * @dev Address of the token for vesting.
     */
    address public constant token = 0x37C997B35C619C21323F3518B9357914E8B99525;
    /**
     * @dev Immutable reference to the token being used in the vesting pools.
     */
    IERC20 private immutable _token;
    /**
     * @dev Maximum percentage allowed for a single vesting pool.
     */
    uint256 private _maxPercentage = 100e18;
    /**
     * @dev Modifier to check that the overall percentage share for a new vesting pool does not exceed 100%.
     */

    modifier checkPercent(uint256 poolShare) {
        if (poolShare == 0) revert InvalidPoolShare();
        if (poolTotalPercent + poolShare > _maxPercentage) revert PoolAmountExceeded();
        _;
    }
    /**
     * @notice Creates a new VestingFactory contract instance.
     */

    constructor() Ownable() {
        _token = IERC20(token);
    }
    /**
     * @notice Creates a new vesting pool with the specified parameters.
     * @param poolShare The percentage share to be allotted for the pool.
     * @param poolName The name of the pool.
     * @return poolAddress The address of the created pool.
     */

    function createPool(
        uint256 poolShare,
        string memory poolName
    )
        external
        onlyOwner
        checkPercent(poolShare)
        returns (address)
    {
        address poolAddress = address(
            new VestingPool{
                salt: keccak256(
                    abi.encodePacked(
                        poolShare,
                        address(this),
                        poolName
                    )
                )
            }(msg.sender)
        );
        poolTotalPercent += poolShare;
        _pools[poolAddress] = PoolInfo(poolShare, 0, poolName);
        emit PoolCreated(poolAddress, poolShare, poolName);
        return poolAddress;
    }
    /**
     * @dev Initializes a vesting pool by transferring the required tokens to the pool contract.
     * @param pool The address of the vesting pool contract to be initialized.
     * @notice The vesting pool must have been created before it can be initialized.
     * @notice The pool must have a positive percentage share and the contract must have enough tokens to initialize the
     * pool.
     */

    function initPool(address pool, uint256 amount) external onlyOwner {
        PoolInfo storage poolInfo = _pools[pool];
        if (poolInfo.poolShare == 0) revert InvalidPoolAddress();
        if (balanceOf() == 0) revert InsufficientTokenAmounts();
        poolInfo.poolAmount = _getPoolShareAmount(poolInfo.poolShare, amount);
        _token.transfer(pool, poolInfo.poolAmount);
        totalDistributed += poolInfo.poolAmount;
        emit PoolInitialized(pool, poolInfo.poolShare, poolInfo.poolAmount);
    }

    /**
     * @dev Recovers ERC20 tokens that were mistakenly sent to the contract.
     * @param tokenAddress The address of the ERC20 token to be recovered.
     * @param tokenAmount The amount of tokens to be recovered.
     * @notice Only the contract owner can recover ERC20 tokens.
     */

    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner {
        IERC20(tokenAddress).transfer(msg.sender, tokenAmount);
    }
    /**
     * @dev Returns the PoolInfo struct of a given vesting pool.
     * @param pool The address of the vesting pool contract to get information about.
     * @return The PoolInfo struct of the given vesting pool.
     */

    function getPoolInfo(address pool) external view returns (PoolInfo memory) {
        return _pools[pool];
    }
    /**
     * @dev Returns the balance of the token held by the contract.
     * @return The balance of the token held by the contract.
     */

    function balanceOf() public view returns (uint256) {
        return IERC20(token).balanceOf(address(this));
    }
    /**
     * @dev Calculates the amount of tokens to be held by a vesting pool based on its percentage share.
     * @param poolShare The percentage share of the token to be alloted for the pool.
     * @return The amount of tokens to be held by the vesting pool.
     */

    function _getPoolShareAmount(uint256 poolShare, uint256 amount) private view returns (uint256) {
        return amount * poolShare / _maxPercentage;
    }
}

// File: src/VestingPool.sol



// SPDX-License-Identifier: MIT
pragma solidity =0.8.11;

/**
 * @title VestingPool
 * @dev A contract that manages token vesting schedules for beneficiaries.
 * It allows an owner to create and manage multiple vesting schedules for different beneficiaries.
 * The vesting schedules can be revocable or non-revocable based on the provided configuration.
 * This contract is compatible with ERC20 tokens.
 */
import "./VestingFactory.sol";
import "./interface/IVestingPool.sol";
import "openzeppelin-contracts/contracts/access/Ownable.sol";
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract VestingPool is IVestingPool, Ownable {
    /**
     * @dev Address of the token for vesting.
     */
    address public constant token = 0x37C997B35C619C21323F3518B9357914E8B99525;
    /**
     * @dev Private variable to store ERC20 token interface
     */
    IERC20 private _token;
    /**
     * @dev Private variable to store total amount of vested tokens
     */
    uint256 private _vestingSchedulesTotalAmount;
    /**
     * @dev Public variable to store total supply
     */
    uint256 public totalSupply;
    /**
     * @dev Public variable to store total released tokens over the time
     */
    uint256 public totalReleased;
    /**
     * @dev Private variable to store an array of vesting schedule beneficiaries
     */
    address[] private _vestingSchedulesBeneficiaries;
    /**
     * @dev Mapping to store vesting schedule information for each beneficiary
     */
    mapping(address => VestingSchedule) private _vestingSchedules;
    /**
     * @dev Mapping to store vesting count for each holder
     */
    mapping(address => uint256) private _holdersVestingCount;
    /**
     * @dev Reverts if the vesting schedule does not exist or has been revoked.
     */

    modifier onlyIfVestingScheduleNotRevoked(address beneficiary) {
        if (_vestingSchedules[beneficiary].initialized == false) revert NotInitialized();
        if (_vestingSchedules[beneficiary].revoked == true) revert AlreadyRevoked();
        _;
    }
    /**
     * @dev Initializes the vesting pool with the provided parameters.
     * @param poolOwner The owner of the vesting pool.
     */

    constructor(address poolOwner) {
        transferOwnership(poolOwner);
        _token = IERC20(token);
    }
    /**
     * @dev Fallback function is executed if none of the other functions match the function
     * identifier or no data was provided with the function call.
     */

    fallback() external { }
    /**
     * @notice Creates a vesting schedule for a beneficiary.
     * @param beneficiary The address of the beneficiary.
     * @param start The start time of the vesting schedule.
     * @param cliff The duration of the cliff in seconds.
     * @param duration The total duration of the vesting schedule in seconds.
     * @param revocable A boolean indicating if the vesting schedule can be revoked.
     * @param amount The total amount of tokens in the vesting schedule.
     */

    function createVestingSchedule(
        address beneficiary,
        uint256 start,
        uint256 cliff,
        uint256 duration,
        bool revocable,
        uint256 amount
    )
        external
        onlyOwner
    {
        if (beneficiary == address(0)) {
            revert AddressCannotBeZero();
        }
        if (duration == 0) revert DurationMustBeGreaterThanZero();
        if (amount == 0) revert AmountMustBeGreaterThanZero();
        if (duration <= cliff) revert DurationMustBeGreaterThanCliff();
        uint256 cliffFromStart = start + cliff;
        _vestingSchedules[beneficiary] =
            VestingSchedule(true, beneficiary, cliffFromStart, start, duration, revocable, amount, 0, false);
        _vestingSchedulesTotalAmount = _vestingSchedulesTotalAmount + amount;
        totalSupply = totalSupply + amount;
        _vestingSchedulesBeneficiaries.push(beneficiary);
        emit Created(beneficiary, amount);
    }
    /**
     * @notice Updates the beneficiary address of a vesting schedule.
     * @param previousBeneficiary The address of the previous beneficiary.
     * @param newBeneficiary The address of the new beneficiary.
     */

    function updateBeneficiary(
        address previousBeneficiary,
        address newBeneficiary
    )
        external
        onlyIfVestingScheduleNotRevoked(previousBeneficiary)
        onlyOwner
    {
        if (newBeneficiary == address(0)) {
            revert AddressCannotBeZero();
        }
        VestingSchedule storage vestingSchedule = _vestingSchedules[previousBeneficiary];
        uint256 releasedAmount = vestingSchedule.released;
        uint256 amountTotal = vestingSchedule.amountTotal;
        bool revocable = vestingSchedule.revocable;
        uint256 start = vestingSchedule.start;
        uint256 cliff = vestingSchedule.cliff;
        uint256 duration = vestingSchedule.duration;
        _vestingSchedules[newBeneficiary] =
            VestingSchedule(true, newBeneficiary, cliff, start, duration, revocable, amountTotal, releasedAmount, false);
        delete _vestingSchedules[previousBeneficiary];
        emit BeneficiaryUpdated(previousBeneficiary, newBeneficiary);
    }

    /**
     * @notice Updates the beneficiary amount for vesting schedule.
     * @param beneficiary The address of the beneficiary.
     * @param amount The updated amount for the beneficiary.
     * * @param increase boolean check to increase the amount or decrease.
     */

    function updateBeneficiaryAmount(
        address beneficiary,
        uint256 amount,
        bool increase
    )
        external
        onlyIfVestingScheduleNotRevoked(beneficiary)
        onlyOwner
    {
        VestingSchedule storage vestingSchedule = _vestingSchedules[beneficiary];
        uint256 previousAmount = vestingSchedule.amountTotal;

        uint256 updatedAmount = increase ? previousAmount + amount : previousAmount - amount;

        if (updatedAmount < vestingSchedule.released) {
            revert InsufficientUpdatedAmount();
        }

        uint256 vestedAmount = _computeReleasableAmount(vestingSchedule);
        if (vestedAmount > 0) {
            _release(beneficiary);
        }

        vestingSchedule.amountTotal = updatedAmount;
        if (increase) {
            totalSupply += amount;
        }
        emit BeneficiaryAmountUpdated(previousAmount, updatedAmount);
    }

    /**
     * @notice Revokes a vesting schedule for a beneficiary.
     * @param beneficiary The address of the beneficiary.
     */

    function revoke(address beneficiary) external onlyOwner onlyIfVestingScheduleNotRevoked(beneficiary) {
        VestingSchedule storage vestingSchedule = _vestingSchedules[beneficiary];
        if (vestingSchedule.revocable == false) revert VestingNotRevocable();
        uint256 vestedAmount = _computeReleasableAmount(vestingSchedule);
        if (vestedAmount > 0) {
            _release(beneficiary);
        }
        uint256 unreleased = vestingSchedule.amountTotal - vestingSchedule.released;
        _vestingSchedulesTotalAmount = _vestingSchedulesTotalAmount - unreleased;
        vestingSchedule.revoked = true;
        if (unreleased > 0) {
            _token.transfer(owner(), unreleased);
        }
    }
    /**
     * @notice Withdraws a specified amount of tokens from the vesting pool.
     * @param amount The amount of tokens to be withdrawn.
     */

    function withdraw(uint256 amount, address recipient) external onlyOwner {
        if (amount > getWithdrawableAmount()) revert NotEnoughWithdrawableFunds();
        _token.transfer(recipient, amount);
    }
    /**
     * @notice Releases the vested tokens for a beneficiary.
     * @param beneficiary The address of the beneficiary.
     */

    function release(address beneficiary) external onlyIfVestingScheduleNotRevoked(beneficiary) {
        _release(beneficiary);
    }
    /**
     * @notice Recovers ERC20 tokens sent to the contract by mistake.
     * @param tokenAddress The address of the ERC20 token.
     * @param tokenAmount The amount of tokens to be recovered.
     */

    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner {
        IERC20(tokenAddress).transfer(msg.sender, tokenAmount);
    }

    /**
     * @notice Returns the vesting schedule of a beneficiary.
     * @param beneficiary The address of the beneficiary.
     * @return The vesting schedule struct.
     */

    function getVestingSchedule(address beneficiary) external view returns (VestingSchedule memory) {
        if (_vestingSchedules[beneficiary].initialized) {
            return _vestingSchedules[beneficiary];
        } else {
            revert BeneficiaryDoesNotExists();
        }
    }
    /**
     * @notice Returns the total amount of tokens in all vesting schedules.
     * @return The total amount of tokens.
     */

    function getVestingSchedulesTotalAmount() external view returns (uint256) {
        return _vestingSchedulesTotalAmount;
    }
    /**
     * @notice Computes the releasable amount of tokens for a beneficiary.
     * @param beneficiary The address of the beneficiary.
     * @return The releasable amount of tokens.
     */

    function computeReleasableAmount(address beneficiary)
        external
        view
        onlyIfVestingScheduleNotRevoked(beneficiary)
        returns (uint256)
    {
        VestingSchedule storage vestingSchedule = _vestingSchedules[beneficiary];
        return _computeReleasableAmount(vestingSchedule);
    }
    /**
     * @notice Computes the total amount of tokens in contract.
     * @return The total amount of tokens.
     */

    function balanceOf() external view returns (uint256) {
        return _balanceOf();
    }

    /**
     * @notice Calculates the amount of tokens that can be withdrawn by the owner.
     * @return The amount of tokens that can be withdrawn.
     */

    function getWithdrawableAmount() public view returns (uint256) {
        if (_balanceOf() > _vestingSchedulesTotalAmount) {
            return _balanceOf() - _vestingSchedulesTotalAmount;
        } else {
            return 0;
        }
    }
    /**
     * @notice Returns the total number of vesting schedules managed by this contract.
     * @return The number of vesting schedules.
     */

    function getVestingSchedulesCount() public view returns (uint256) {
        return _vestingSchedulesBeneficiaries.length;
    }
    /**
     * @dev Releases the vested amount of tokens for the specified beneficiary.
     * @param beneficiary The address of the beneficiary whose tokens will be released.
     */

    function _release(address beneficiary) private onlyIfVestingScheduleNotRevoked(beneficiary) {
        VestingSchedule storage vestingSchedule = _vestingSchedules[beneficiary];
        uint256 vestedAmount = _computeReleasableAmount(vestingSchedule);
        uint256 releasedAmount = vestingSchedule.released;
        if (vestedAmount > 0) {
            vestingSchedule.released = releasedAmount + vestedAmount;
            totalReleased = totalReleased + vestedAmount;
            _vestingSchedulesTotalAmount -= vestedAmount;
            _token.transfer(beneficiary, vestedAmount);
            emit Released(beneficiary, vestedAmount);
        }
    }
    /**
     * @dev Calculates the releasable amount of tokens for a given vesting schedule.
     * @param vestingSchedule The vesting schedule to compute the releasable amount for.
     * @return The amount of releasable tokens.
     */

    function _computeReleasableAmount(VestingSchedule memory vestingSchedule) private view returns (uint256) {
        uint256 currentTime = block.timestamp;
        if ((currentTime < vestingSchedule.cliff) || vestingSchedule.revoked) {
            return 0;
        } else if (currentTime >= vestingSchedule.start + vestingSchedule.duration) {
            return vestingSchedule.amountTotal - vestingSchedule.released;
        } else {
            uint256 timeFromStart = currentTime - vestingSchedule.start;
            uint256 vestedAmount = (vestingSchedule.amountTotal / vestingSchedule.duration) * timeFromStart;
            return vestedAmount - vestingSchedule.released;
        }
    }
    /**
     * @dev Retrieves the contract balance of the vested token.
     * @return The current balance of vested tokens.
     */

    function _balanceOf() private view returns (uint256) {
        return _token.balanceOf(address(this));
    }
}

// File: src/interface/IVestingFactory.sol



// SPDX-License-Identifier: MIT
pragma solidity =0.8.11;

interface IVestingFactory {
    /**
     * @dev Error thrown when an invalid pool share is provided
     */
    error InvalidPoolShare();

    /**
     * @dev Error thrown when pool amount exceeds the total available tokens
     */
    error PoolAmountExceeded();

    /**
     * @dev Error thrown when an invalid pool address is provided
     */
    error InvalidPoolAddress();

    /**
     * @dev Error thrown when there are insufficient token amounts
     */
    error InsufficientTokenAmounts();

    /**
     * @dev Stores information about each vesting pool.
     * @param poolShare The percentage share of the pool.
     * @param poolAmount The amount of tokens in the pool.
     * @param poolName The name of the pool.
     */
    struct PoolInfo {
        uint256 poolShare;
        uint256 poolAmount;
        string poolName;
    }

    /**
     * @dev Emitted when a new vesting pool is created.
     * @param poolAddress The address of the newly created pool.
     * @param poolShare The percentage share of the pool.
     * @param poolName The name of the pool.
     */
    event PoolCreated(address indexed poolAddress, uint256 poolShare, string poolName);

    /**
     * @dev Emitted when a vesting pool is initialized.
     * @param poolAddress The address of the initialized pool.
     * @param poolShare The percentage share of the pool.
     * @param poolAmount The amount of tokens in the pool.
     */
    event PoolInitialized(address indexed poolAddress, uint256 poolShare, uint256 poolAmount);

    /**
     * @dev Emitted when a vesting pool is updated.
     * @param poolAddress The address of the updated pool.
     * @param poolShare The updated percentage share of the pool.
     * @param poolAmount The updated amount of tokens in the pool.
     * @param poolName The name of the pool.
     */
    event PoolUpdated(address indexed poolAddress, uint256 poolShare, uint256 poolAmount, string poolName);
}

// File: src/interface/IVestingPool.sol



// SPDX-License-Identifier: MIT
pragma solidity =0.8.11;

/**
 * @title Vesting Pool Interface
 * @dev Interface for a vesting pool with common functionalities.
 */

interface IVestingPool {
    /**
     * @dev AddressCannotBeZero - Error thrown when the provided address is zero.
     */
    error AddressCannotBeZero();
    /**
     * @dev DurationMustBeGreaterThanZero - Error thrown when the duration of the vesting schedule is set to zero.
     */
    error DurationMustBeGreaterThanZero();
    /**
     * @dev AmountMustBeGreaterThanZero - Error thrown when the amount of tokens for vesting is set to zero.
     */
    error AmountMustBeGreaterThanZero();
    /**
     *  @dev DurationMustBeGreaterThanCliff - Error thrown when the duration of the vesting schedule is less than or
     * equal to the cliff period.
     */
    error DurationMustBeGreaterThanCliff();
    /**
     * @dev VestingNotRevocable - Error thrown when attempting to revoke a non-revocable vesting schedule.
     */
    error VestingNotRevocable();
    /**
     * @dev NotEnoughWithdrawableFunds - Error thrown when attempting to withdraw more funds than available.
     */

    error NotEnoughWithdrawableFunds();

    /**
     * @dev BeneficiaryDoesNotExists - Error thrown when the beneficiary does not exist.
     */
    error BeneficiaryDoesNotExists();

    /**
     * @dev IndexOutOfBound - Error thrown when attempting to access an index that is out of bounds.
     */

    error IndexOutOfBound();

    /**
     * @dev OnlyBeneficiaryAndOwnerCanReleaseVestedTokens - Error thrown when a non-beneficiary or non-owner attempts to
     * release vested tokens.
     */
    error OnlyBeneficiaryAndOwnerCanReleaseVestedTokens();

    /**
     * @dev NotEnoughVestedTokens - Error thrown when there are not enough vested tokens to release.
     */
    error NotEnoughVestedTokens();

    /**
     * @dev NotInitialized - Error thrown when a vesting schedule is not initialized.
     */
    error NotInitialized();

    /**
     * @dev AlreadyRevoked - Error thrown when a vesting schedule has already been revoked.
     */
    error AlreadyRevoked();

    /**
     * @dev InsufficientUpdatedAmount - Error thrown when a wrong inputs are entered.
     */
    error InsufficientUpdatedAmount();

    /**
     *  @dev VestingSchedule - Struct representing a vesting schedule.
     *  @param initialized: Indicates if the vesting schedule is initialized.
     *  @param beneficiary: The beneficiary of the tokens after they are released.
     *  @param cliff: The cliff period in seconds.
     *  @param start: The start time of the vesting period.
     *  @param duration: The duration of the vesting period in seconds.
     *  @param revocable: Indicates if the vesting schedule is revocable.
     *  @param amountTotal: The total amount of tokens to be released at the end of the vesting.
     *  @param released: The amount of tokens already released.
     *  @param revoked: Indicates if the vesting schedule has been revoked.
     */

    struct VestingSchedule {
        bool initialized;
        address beneficiary;
        uint256 cliff;
        uint256 start;
        uint256 duration;
        bool revocable;
        uint256 amountTotal;
        uint256 released;
        bool revoked;
    }

    /**
     * @dev Struct containing information about the vesting pool.
     * @param poolShare The percentage share of the vesting pool.
     * @param distributer The address of the token distributer.
     * @param tokenAmount The total amount of tokens in the vesting pool.
     * @param poolName The name of the vesting pool.
     */

    struct PoolInfo {
        uint256 poolShare;
        address distributer;
        uint256 tokenAmount;
        string poolName;
    }
    /**
     *    @dev Created - Emitted when a new vesting schedule is created.
     * @param beneficiary: The beneficiary of the tokens after they are released.
     * @param amountTotal: The total amount of tokens to be released at the end of the vesting.
     */

    event Created(address indexed beneficiary, uint256 amountTotal);
    /**
     * @dev Released - Emitted when vested tokens are released
     *   @param beneficiary: The beneficiary of the released tokens.
     *   @param amount: The amount of tokens released.
     */
    event Released(address indexed beneficiary, uint256 amount);

    /**
     * @dev BeneficiaryUpdated - Emitted when beneficiary is updated
     *   @param previousBeneficiary: previous address of beneficiary.
     *   @param newBeneficiary: new address of beneficiary.
     */
    event BeneficiaryUpdated(address previousBeneficiary, address newBeneficiary);

    /**
     * @dev BeneficiaryAmountUpdated - Emitted when beneficiary tokens are updated
     *   @param previousAmount: Previous amounts of beneficiary.
     *   @param newAmount: New amount of beneficiary.
     */
    event BeneficiaryAmountUpdated(uint256 previousAmount, uint256 newAmount);
}