// File: @chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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 Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        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: @openzeppelin/contracts/access/Ownable2Step.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides 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} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() external {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}

// File: @openzeppelin/contracts/interfaces/IERC20Metadata.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/extensions/IERC20Metadata.sol";

// File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol



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

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol



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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// File: @openzeppelin/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: @openzeppelin/contracts/utils/introspection/IERC165.sol



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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: contracts/interfaces/IAggregationRouterV5.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IAggregationExecutor {
    function execute(address msgSender) external payable;
}

interface IAggregationRouterV5 {
    /// @dev swap data for 1inch when claiming rewards to perform a swap
    struct SwapDescription {
        IERC20 srcToken;
        IERC20 dstToken;
        address payable srcReceiver;
        address payable dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
    }

    /// @dev swap transaction data
    struct SwapTransaction {
        IAggregationExecutor executor;
        SwapDescription description;
        bytes permit;
        bytes data;
    }

    function swap(
        IAggregationExecutor caller,
        SwapDescription calldata desc,
        bytes calldata permit,
        bytes calldata data
    ) external payable returns (uint256 returnAmount, uint256 spentAmount);
}

// File: contracts/interfaces/IBondCalculator.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IBondCalculator {
    function computeRoi(
        uint256 timestamp,
        uint256 bondRoundStartingTime,
        uint256 totalPeriodsNumber,
        uint256 composedFunction,
        uint256 maxCvgToMint,
        uint256 cvgMintedOnActualRound,
        uint256 minRoi,
        uint256 maxRoi
    ) external pure returns (uint256);
}

// File: contracts/interfaces/IBondDepository.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ICvgControlTower.sol";
import "./IBondStruct.sol";
import "./ICvgOracle.sol";

interface IBondDepository {
    // Deposit Principle token in Treasury through Bond contract
    function deposit(uint256 tokenId, uint256 amount, address receiver) external;

    function depositToLock(uint256 amount, address receiver) external returns (uint256 cvgToMint);

    function getBondView() external view returns (IBondStruct.BondView memory);

    function bondInfos(uint256 tokenId) external view returns (IBondStruct.BondPending memory);

    function getTokenVestingInfo(uint256 tokenId) external view returns (IBondStruct.TokenVestingInfo memory);

    function redeem(uint256 tokenId, address recipient, address operator) external returns (uint256);

    function bondParams() external view returns (IBondStruct.BondParams memory);

    function initialize(
        ICvgControlTower _cvgControlTower,
        IBondStruct.BondParams calldata _bondParams,
        IOracleStruct.OracleParams calldata _oracleParams
    ) external;
}

// File: contracts/interfaces/IBondLogo.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

interface IBondLogo {
    struct LogoInfos {
        uint256 tokenId;
        uint256 termTimestamp;
        uint256 pending;
        uint256 cvgClaimable;
        uint256 unlockingTimestamp;
    }
    struct LogoInfosFull {
        uint256 tokenId;
        uint256 termTimestamp;
        uint256 pending;
        uint256 cvgClaimable;
        uint256 unlockingTimestamp;
        uint256 year;
        uint256 month;
        uint256 day;
        uint256 hoursLock;
        uint256 cvgPrice;
    }

    function _tokenURI(LogoInfos memory logoInfos) external pure returns (string memory output);
}

// File: contracts/interfaces/IBondPositionManager.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IBondStruct.sol";
import "./IBondLogo.sol";

interface IBondPositionManager {
    // Deposit Principle token in Treasury through Bond contract
    function mint(address account) external;

    function burn(uint256 tokenId) external;

    function nextId() external view returns (uint256);

    function ownerOf(uint256 tokenId) external view returns (address);

    function bondPerTokenId(uint256 tokenId) external view returns (address);

    function unlockingTimestampPerToken(uint256 tokenId) external view returns (uint256);

    function logoInfo(uint256 tokenId) external view returns (IBondLogo.LogoInfos memory);
}

// File: contracts/interfaces/IBondStruct.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IBondStruct {
    struct BondParams {
        bool isStable;
        uint8 numberOfPeriod;
        uint16 composedFunction;
        address token;
        uint32 minRoi; // Min bond ROI, divide by 1000 to get the roi in %
        uint32 maxRoi; // Max bond ROI, divide by 1000 to get the roi in %
        uint128 percentageMaxCvgToMint; // Percentage maximum of the maxCvgToMint that an user can mint in one deposit
        uint128 vestingTerm; // In timestamp
        uint256 maxCvgToMint; // Limit of Max CVG to mint
    }
    struct BondPending {
        uint128 lastBlock; // Last interaction
        uint128 vesting; // vesting left
        uint256 payout; // CVG remaining to be paid
    }
    struct BondTokenView {
        uint128 lastBlock;
        uint128 term;
        uint256 claimableCvg;
        uint256 vestedCvg;
    }

    struct BondView {
        address bondAddress;
        uint40 vestingTerm;
        uint40 bondRoi;
        bool isFlexible;
        bool isValid;
        uint256 totalCvgMinted;
        uint256 maxCvgToMint;
        uint256 assetPriceUsdCvgOracle;
        uint256 assetPriceUsdAggregator;
        uint256 bondPriceAsset;
        uint256 bondPriceUsd;
        ERC20View token;
    }
    struct ERC20View {
        string token;
        address tokenAddress;
        uint256 decimals;
    }
    struct TokenVestingInfo {
        uint256 term;
        uint256 claimable;
        uint256 pending;
    }
}

// File: contracts/interfaces/ICvg.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";

interface ICvg is IERC20Metadata {
    function mintBond(address account, uint256 amount) external;

    function mintStaking(address account, uint256 amount) external;
}

// File: contracts/interfaces/ICvgControlTower.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import "./IERC20Mintable.sol";
import "./ICvg.sol";
import "./IBondDepository.sol";
import "./IBondCalculator.sol";
import "./IBondStruct.sol";
import "./ITAssetStruct.sol";
import "./ICvgOracle.sol";
import "./IVotingPowerEscrow.sol";
import "./ICvgRewards.sol";
import "./ILockingPositionManager.sol";
import "./ILockingPositionDelegate.sol";
import "./IGaugeController.sol";
import "./ITokeManager.sol";
import "./ITokeRewards.sol";
import "./ICvgTokeStaking.sol";
import "./ITokeStaker.sol";
import "./ITAssetStaking.sol";
import "./IYsDistributor.sol";
import "./ITAssetBlackHole.sol";
import "./IBondPositionManager.sol";
import "./ISwapperFactory.sol";
import "./IStakingPositionManager.sol";
import "./IStakingViewer.sol";
import "./IStakingLogo.sol";
import "./IBondLogo.sol";
import "./ILockingLogo.sol";
import "./ILockingPositionService.sol";

interface ICvgControlTower {
    function cvgToken() external view returns (ICvg);

    function cvgOracle() external view returns (ICvgOracle);

    function bondCalculator() external view returns (IBondCalculator);

    function gaugeController() external view returns (IGaugeController);

    function cvgCycle() external view returns (uint128);

    function votingPowerEscrow() external view returns (IVotingPowerEscrow);

    function allBaseBonds(uint256 index) external view returns (address);

    function allBaseAggregators(uint256 index) external view returns (address);

    function allBaseTAssetStakings(uint256 index) external view returns (address);

    function treasuryDao() external view returns (address);

    function treasuryStaking() external view returns (address);

    function treasuryBonds() external view returns (address);

    function insertNewBond(address _newClone, uint256 _version) external;

    function insertNewTAssetStaking(address _newClone, uint256 _version) external;

    function insertNewAggregator(address _newClone, uint256 _version) external;

    function cvgRewards() external view returns (ICvgRewards);

    function tokeStaker() external view returns (ITokeStaker);

    function cvgTokeStaking() external view returns (ICvgTokeStaking);

    function lockingPositionManager() external view returns (ILockingPositionManager);

    function lockingPositionService() external view returns (ILockingPositionService);

    function lockingPositionDelegate() external view returns (ILockingPositionDelegate);

    function tokeManager() external view returns (ITokeManager);

    function tokeRewards() external view returns (ITokeRewards);

    function toke() external view returns (IERC20Metadata);

    function cvgToke() external view returns (IERC20Mintable);

    function poolCvgToke() external view returns (address);

    function getTAssetStakingContract(uint256 index) external view returns (address);

    function isTAssetStaking(address contractAddress) external view returns (bool);

    function isStakingContract(address contractAddress) external view returns (bool);

    function getTAssetStakingLength() external view returns (uint256);

    function ysDistributor() external view returns (IYsDistributor);

    function tAssetBlackHole() external view returns (ITAssetBlackHole);

    function isBond(address account) external view returns (bool);

    function bondPositionManager() external view returns (IBondPositionManager);

    function stakingPositionManager() external view returns (IStakingPositionManager);

    function stakingLogo() external view returns (IStakingLogo);

    function bondLogo() external view returns (IBondLogo);

    function lockingLogo() external view returns (ILockingLogo);

    function cvgUtilities() external view returns (address);

    function swapperFactory() external view returns (ISwapperFactory);
}

// File: contracts/interfaces/ICvgOracle.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IOracleStruct.sol";

interface ICvgOracle {
    function getPriceOracle(IOracleStruct.OracleParams calldata oracleParams) external view returns (uint256, bool);

    function getCvgPriceOracleUnverified() external view returns (uint256);

    function getEthPriceOracleUnverified() external view returns (uint256);

    function getAndVerifyCvgPrice() external view returns (uint256, bool);

    function getPriceAggregator(AggregatorV3Interface aggregator) external view returns (uint256, uint256);

    function getAndVerifyPrice(
        IOracleStruct.OracleParams memory oracleParams,
        bool isStable
    ) external view returns (uint256, bool);
}

// File: contracts/interfaces/ICvgRewards.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ICvgRewards {
    function cvgCycleRewards() external view returns (uint256);
}

// File: contracts/interfaces/ICvgTokeStaking.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

interface ICvgTokeStaking is IERC721Enumerable {
    //structs
    struct CycleTokenInfo {
        uint256 amountStaked;
        bool rewardsClaimed;
    }
    struct CycleInfo {
        uint256[] rewardsAmounts;
        uint256 totalStaked;
        mapping(uint256 => CycleTokenInfo) nftInfo;
    }
    struct TokenStakingInfo {
        uint256[] cvgActionCycleIds;
        uint256[] tokeActionCycleIds;
    }

    function getRewardsForTokeCycle(uint256 _cycleId) external view returns (uint256[] memory);

    function getTokenInfoForCycle(uint256 tokenId, uint256 _cycleId) external view returns (CycleTokenInfo memory);

    function assets() external view returns (uint256);

    function cvgStakingCycle() external view returns (uint256);

    function tokeStakingCycle() external view returns (uint256);

    function tokenTotalStaked(uint256 tokenId) external view returns (uint256 amount);

    function findTokenStakedAmountForCvgCycle(uint256 tokenId, uint256 _cycleId) external view returns (uint256);

    function findTokenStakedAmountForTokeCycle(uint256 tokenId, uint256 _cycleId) external view returns (uint256);

    //external
    function deposit(uint256 tokenId, uint256 amount, address operator) external;

    function withdraw(uint256 tokenId, uint256 amount) external;

    function claimCvgRewards(uint256 tokenId, uint256 _cycleId) external;

    function claimTokeRewards(uint256 tokenId, uint256 _cycleId) external;

    function claimMultipleCvgRewards(uint256 tokenId, uint256[] memory _cycleIds, address operator) external;

    function claimMultipleTokeRewards(
        uint256 tokenId,
        uint256[] memory _cycleIds,
        address operator,
        bool _isConvert,
        bool _isMint
    ) external;

    //OnlyStaker
    function updateTokeStakingCycle() external;

    function processTokeRewards(uint256[] memory amounts, uint256 tokeStakingCycle) external;

    //OnlyOwner
    function togglePause() external;
}

// File: contracts/interfaces/IERC20Mintable.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";

/**
 * @dev Interface for the optional mint function from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Mintable is IERC20Metadata {
    /**
     * @dev Mint `amount` of token to `account`
     */
    function mint(address account, uint256 amount) external;
}

// File: contracts/interfaces/IGaugeController.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IGaugeController {
    function add_type(string memory typeName, uint256 weight) external;

    function add_gauge(address addr, int128 gaugeType, uint256 weight) external;

    function get_gauge_weight(address gaugeAddress) external view returns (uint256);

    function get_total_weight() external view returns (uint256);

    function n_gauges() external view returns (uint128);

    function gauges(uint256 index) external view returns (address);

    function gauge_types(address gaugeAddress) external view returns (int128);

    function get_type_weight(int128 typeId) external view returns (uint256);

    function gauge_relative_weight(address addr, uint256 time) external view returns (uint256);

    function set_lock(bool isLock) external;

    function gauge_relative_weight_write(address gaugeAddress) external;

    function simple_vote(uint256 tokenId, address gaugeAddress, uint256 tokenWeight) external;
}

// File: contracts/interfaces/ILockingLogo.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ILockingLogo {
    struct LogoInfos {
        uint256 tokenId;
        uint256 cvgLocked;
        uint256 lockEnd;
        uint256 ysPercentage;
        uint256 mgCvg;
        uint256 unlockingTimestamp;
    }
    struct GaugePosition {
        uint256 ysWidth; // width of the YS gauge part
        uint256 veWidth; // width of the VE gauge part
    }

    struct LogoInfosFull {
        uint256 tokenId;
        uint256 cvgLocked;
        uint256 lockEnd;
        uint256 ysPercentage;
        uint256 mgCvg;
        uint256 unlockingTimestamp;
        uint256 cvgLockedInUsd;
        uint256 ysCvg;
        uint256 veCvg;
        GaugePosition gaugePosition;
        uint256 claimableInUsd;
        uint256 hoursLock;
    }

    function _tokenURI(LogoInfos memory logoInfos) external pure returns (string memory output);
}

// File: contracts/interfaces/ILockingPositionDelegate.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ILockingPositionDelegate {
    struct OwnedAndDelegated {
        uint256[] owneds;
        uint256[] mgDelegateds;
        uint256[] veDelegateds;
    }

    function delegatedYsCvg(uint256 tokenId) external view returns (address);

    function getMgDelegateeInfoPerTokenAndAddress(
        uint256 _tokenId,
        address _to
    ) external view returns (uint256, uint256, uint256);

    function getVeCvgIndexForDelegatee(address _delegatee, uint256 _tokenId) external view returns (uint256);

    function getMgCvgIndexForDelegatee(address _delegatee, uint256 _tokenId) external view returns (uint256);

    function delegateVeCvg(uint256 _tokenId, address _to) external;

    function delegateYsCvg(uint256 _tokenId, address _to, bool _status) external;

    function delegateMgCvg(uint256 _tokenId, address _to, uint256 _percentage) external;

    function delegatedVeCvg(uint256 tokenId) external view returns (address);

    function getVeCvgDelegatees(address account) external view returns (uint256[] memory);

    function getMgCvgDelegatees(address account) external view returns (uint256[] memory);

    function getTokenOwnedAndDelegated(address _addr) external view returns (OwnedAndDelegated[] memory);

    function getTokenMgOwnedAndDelegated(address _addr) external view returns (uint256[] memory, uint256[] memory);

    function getTokenVeOwnedAndDelegated(address _addr) external view returns (uint256[] memory, uint256[] memory);

    function addTokenAtMint(uint256 _tokenId, address minter) external;
}

// File: contracts/interfaces/ILockingPositionManager.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ILockingLogo.sol";

interface ILockingPositionManager {
    function ownerOf(uint256 tokenId) external view returns (address);

    function owner() external view returns (address);

    function mint(address account) external;

    function burn(uint256 tokenId) external;

    function nextId() external view returns (uint256);

    function getComplianceInfo(uint256 tokenId) external view returns (address, uint256);

    function logoInfo(uint256 tokenId) external view returns (ILockingLogo.LogoInfos memory);

    function unlockingTimestampPerToken(uint256 tokenId) external view returns (uint256);
}

// File: contracts/interfaces/ILockingPositionService.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ILockingPositionService {
    struct LockingPosition {
        uint96 startCycle; /// @dev Cycle of the start of the locking
        uint96 lastEndCycle; /// @dev  Cycle of the end of the locking
        uint64 ysPercentage; /// @dev  Percentage of the NFT dedicated to ysCvg
        uint256 totalCvgLocked; /// @dev  Number of CVG Locked
        uint256 mgCvgAmount; /// @dev  Meta Governance CVG amount Max
    }

    struct LockingInfo {
        uint256 tokenId;
        uint256 cvgLocked;
        uint256 lockEnd;
        uint256 ysPercentage;
        uint256 mgCvg;
    }

    function TDE_DURATION() external view returns (uint256);

    function updateYsTotalSupply() external;

    function ysTotalSupply() external view returns (uint256);

    function ysTotalSupplyHistory(uint256) external view returns (uint256);

    function ysShareOnTokenAtTde(uint256, uint256) external view returns (uint256);

    function votingPowerPerAddress(address _user) external view returns (uint256);

    function mintPosition(
        uint96 lockDuration,
        uint256 amount,
        uint64 ysPercentage,
        address receiver,
        bool isAddToManagedTokens
    ) external;

    function increaseLockAmount(uint256 tokenId, uint256 amount, address operator) external;

    function increaseLockTime(uint256 tokenId, uint256 durationAdd) external;

    function increaseLockTimeAndAmount(uint256 tokenId, uint256 durationAdd, uint256 amount, address operator) external;

    function totalSupplyYsCvgHistories(uint256 cycleClaimed) external view returns (uint256);

    function balanceOfYsCvgAt(uint256 tokenId, uint256 cycle) external view returns (uint256);

    function lockingPositions(uint256 tokenId) external view returns (LockingPosition memory);

    function unlockingTimestampPerToken(uint256 tokenId) external view returns (uint256);

    function lockingInfo(uint256 tokenId) external view returns (LockingInfo memory);

    function isContractLocker(address contractAddress) external view returns (bool);
}

// File: contracts/interfaces/IOracleStruct.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

interface IOracleStruct {
    struct OracleParams {
        uint48 poolType;
        address poolAddress;
        bool isReversed;
        bool isEthPriceRelated;
        uint32 twapInterval;
        uint48 deltaAggregatorCvgOracle; // 5 % => 500 & 100 % => 10 000
        uint48 maxLastUpdateAggregator; // Buffer time before a not updated price is considered as stale
        AggregatorV3Interface aggregatorOracle;
    }
}

// File: contracts/interfaces/IPresaleCvgSeed.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

interface IPresaleCvgSeed is IERC721Enumerable {
    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            ENUMS & STRUCTS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    enum SaleState {
        NOT_ACTIVE,
        PRESEED,
        SEED,
        OVER
    }

    struct PresaleInfo {
        uint256 vestingType; // Define the presaler type
        uint256 cvgAmount; // Total CVG amount claimable for the nft owner
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            SETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function setSaleState(SaleState _saleState) external;

    function grantPreseed(address _wallet, uint256 _amount) external;

    function grantSeed(address _wallet, uint256 _amount) external;

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            EXTERNALS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function investMint(bool _isDai) external;

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            GETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function presaleInfoTokenId(uint256 _tokenId) external view returns (PresaleInfo memory);

    function saleState() external view returns (SaleState);

    function tokenOfOwnerByIndex(address owner, uint256 index) external view override returns (uint256);

    function getTokenIdAndType(
        address _wallet,
        uint256 _index
    ) external view returns (uint256 tokenId, uint256 typeVesting);

    function getTokenIdsForWallet(address _wallet) external view returns (uint256[] memory);

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                        WITHDRAW OWNER
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function withdrawFunds() external;

    function withdrawToken(address _token) external;
}

// File: contracts/interfaces/IStakingLogo.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IStakingLogo {
    struct LogoInfos {
        uint256 tokenId;
        string symbol;
        uint256 pending;
        uint256 totalStaked;
        uint256 cvgClaimable;
        uint256[] tokeClaimable;
        uint256 unlockingTimestamp;
    }

    struct LogoInfosFull {
        uint256 tokenId;
        string symbol;
        uint256 pending;
        uint256 totalStaked;
        uint256 cvgClaimable;
        uint256[] tokeClaimable;
        uint256 unlockingTimestamp;
        uint256 claimableInUsd;
        uint256 hoursLock;
    }

    function _tokenURI(LogoInfos memory logoInfos) external pure returns (string memory output);
}

// File: contracts/interfaces/IStakingPositionManager.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IStakingLogo.sol";

interface IStakingPositionManager {
    function mint(address account) external;

    function burn(uint256 tokenId) external;

    function nextId() external view returns (uint256);

    function ownerOf(uint256 tokenId) external view returns (address);

    function getComplianceInfo(uint256 tokenId) external view returns (address, address, uint256);

    function stakingPerTokenId(uint256 tokenId) external view returns (address);

    function unlockingTimestampPerToken(uint256 tokenId) external view returns (uint256);

    function logoInfo(uint256 tokenId) external view returns (IStakingLogo.LogoInfos memory);
}

// File: contracts/interfaces/IStakingViewer.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ITAssetStruct.sol";

interface IStakingViewer {
    function getGlobalViewTokeStaking(
        address stakingContract
    ) external pure returns (ITAssetStruct.TAssetGlobalView memory);
}

// File: contracts/interfaces/ISwapperFactory.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IAggregationRouterV5.sol";

interface ISwapperFactory {
    function swap(IAggregationRouterV5.SwapTransaction calldata) external;

    function enableTAssetForSwap(address _tAsset, address _stakingContract) external;

    function executeSwapForCvgToke(address _user, IAggregationRouterV5.SwapTransaction calldata) external returns (uint256);

    function executeSimpleSwap(address _user, IAggregationRouterV5.SwapTransaction calldata) external returns (uint256);
}
// File: contracts/interfaces/ITAssetBlackHole.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IPresaleCvgSeed.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ITAssetBlackHole {
    function withdraw(IERC20 tAsset, address receiver, uint256 amount) external;
}

// File: contracts/interfaces/ITAssetStaking.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

import "./ITokeRewards.sol";
import "./ITAssetStruct.sol";
import "./ICvgControlTower.sol";
import "./IStakingLogo.sol";

interface ITAssetStaking is IERC721Enumerable {
    struct StakingInfo {
        uint256 tokenId;
        string symbol;
        uint256 pending;
        uint256 totalStaked;
        uint256 cvgClaimable;
        uint256[] tokeClaimable;
    }

    function processTokeRewards(uint256 amount, uint256 tokeCycle) external;

    function getGlobalViewTokeStaking() external view returns (ITAssetStruct.TAssetGlobalView memory);

    function deposit(uint256 tokenId, uint256 amount, address operator) external;

    function initialize(ICvgControlTower _cvgControlTower, IERC20Metadata _tAsset, string memory setSymbol) external;

    function claimMultipleCvgRewards(uint256 tokenId, uint256[] memory _cycleIds) external;

    function claimMultipleTokeRewards(
        uint256 tokenId,
        uint256[] memory _cycleIds,
        bool _isConvert,
        bool _isMint
    ) external;

    function stakingInfo(uint256 tokenId) external view returns (StakingInfo memory);

    function checkBurn(uint256 tokenId) external view;
}

// File: contracts/interfaces/ITAssetStruct.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../interfaces/ITokeRewards.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ITAssetStruct {
    struct TAssetGlobalView {
        ERC20View stakingAsset;
        address stakingAddress;
        uint256 cvgCycle;
        uint256 tokeCycle;
        uint256 previousCvgCycleTotal;
        uint256 previousTokeCycleTotal;
        uint256 actualStakedCvgCycleTotal;
        uint256 actualStakedTokeCycleTotal;
        uint256 nextStakedCvgCycleTotal;
        uint256 nextStakedTokeCycleTotal;
    }

    struct TAssetTokenView {
        uint256 previousCvgCycleToken;
        uint256 previousTokeCycleToken;
        uint256 actualStakedCvgCycleToken;
        uint256 actualStakedTokeCycleToken;
        uint256 nextStakedCvgCycleToken;
        uint256 nextStakedTokeCycleToken;
    }
    struct ERC20View {
        string token;
        address tokenAddress;
        uint256 decimals;
    }
}

// File: contracts/interfaces/ITokeManager.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ITokeManager {
    function currentCycleIndex() external view returns (uint256);

    // NOTE: THIS IS ONLY FOR TESTS PURPOSE, THIS FUNCTION DOES NOT EXIST IN REALITY
    function incrementCycleIndex(uint256 _value) external;
}

// File: contracts/interfaces/ITokeRewards.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ITokeRewards {
    struct Recipient {
        uint256 chainId;
        uint256 cycle;
        address wallet;
        uint256 amount;
    }
    struct ClaimData {
        Recipient recipient;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    function getClaimableAmount(Recipient calldata recipient) external view returns (uint256);

    function claim(Recipient calldata recipient, uint8 v, bytes32 r, bytes32 s) external;
}

// File: contracts/interfaces/ITokeStaker.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ITokeRewards.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ITokeStaker {
    //structs
    struct Staking {
        uint256 amountToke;
        uint256 numCycle;
    }

    struct DepositsInfo {
        address addr;
        bytes signature;
    }

    struct RewardsInfo {
        ITokeRewards rewardContract;
        IERC20 rewardAsset;
    }

    //getters
    function getRewardsInfo() external view returns (RewardsInfo[] memory);

    function getDepositsInfo() external view returns (DepositsInfo[] memory);

    //onlyOwner
    function addAssetInfo(RewardsInfo memory _rewardInfo, DepositsInfo memory _depositInfo) external;

    function removeLastAssetInfo() external;

    function setDepositInfo(uint256 element, DepositsInfo memory _depositInfo) external;

    function setRewardInfo(uint256 element, RewardsInfo memory _rewardsInfo) external;

    function stakeAndUpdateTokeCycle(Staking[] calldata _staking) external;

    function depositAndProcessTokeRewards(ITokeRewards.ClaimData[] calldata claimData) external;

    function withdrawToken(IERC20 _token) external;
}

// File: contracts/interfaces/IVotingPowerEscrow.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVotingPowerEscrow {
    function create_lock(uint256 tokenId, uint256 value, uint256 unlockTime) external;

    function increase_amount(uint256 tokenId, uint256 value) external;

    function increase_unlock_time(uint256 tokenId, uint256 unlockTime) external;

    function withdraw(uint256 tokenId) external;

    function total_supply() external returns (uint256);

    function balanceOf(uint256 tokenId) external view returns (uint256);
}

// File: contracts/interfaces/IYsDistributor.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IAggregationRouterV5.sol";
import "./IOracleStruct.sol";

interface IYsDistributor {
    function getTokensDepositedAtTde(uint256 _tdeId) external view returns (address[] memory);

    function tokenOracleParams(address _token) external view returns (IOracleStruct.OracleParams memory);

    function claimRewards(
        uint256 tokenId,
        uint256 tdeId,
        address receiver,
        address operator,
        IAggregationRouterV5.SwapTransaction[] calldata _swapTransactions,
        IERC20 destinationToken
    ) external;

    function getTokenRewardAmountForTde(IERC20 _token, uint256 tdeId, uint256 share) external view returns (uint256);

    function rewardsClaimedForToken(uint256 tokenId, uint256 tdeId) external view returns (bool);
}

// File: contracts/Rewards/CvgQuest.sol



// SPDX-License-Identifier: MIT
/**
 _____
/  __ \
| /  \/ ___  _ ____   _____ _ __ __ _  ___ _ __   ___ ___
| |    / _ \| '_ \ \ / / _ \ '__/ _` |/ _ \ '_ \ / __/ _ \
| \__/\ (_) | | | \ V /  __/ | | (_| |  __/ | | | (_|  __/
 \____/\___/|_| |_|\_/ \___|_|  \__, |\___|_| |_|\___\___|
                                 __/ |
                                |___/
 */
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "../interfaces/ICvgControlTower.sol";

contract CvgQuest is Ownable2Step {
    struct Quest {
        uint256 rewardAmount;
        IERC20 rewardToken;
        bool solved;
        uint88 number;
        IERC721 rewardNft;
        uint256 rewardTokenId;
    }

    struct QuestView {
        Quest quest;
        bytes32 answer;
    }

    /// @dev quests data
    mapping(bytes32 => Quest) public quests; // answer => data

    /// @dev quests data for view
    QuestView[] public questData;

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            CONSTRUCTOR
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    constructor(address _treasuryDao, bytes32[] memory _answers, Quest[] memory _quests) {
        require(_answers.length == _quests.length, "DATA_LENGTH_MISMATCH");

        /// @dev save quests
        for (uint256 i; i < _answers.length;) {
            quests[_answers[i]] = _quests[i];
            questData.push(QuestView({ quest: _quests[i], answer: _answers[i] }));
            unchecked { ++i; }
        }

        /// @dev transfer ownership to treasury DAO
        _transferOwnership(_treasuryDao);
    }

    /**
     *  @notice Submits a quest answer and send rewards to user if answer is correct
     *  @param _stringAnswer user's answer
     */
    function submitAnswer(string memory _stringAnswer) external {
        bytes32 _answer = keccak256(abi.encodePacked(_stringAnswer));
        Quest memory _quest = quests[_answer];

        require(_quest.rewardAmount > 0 || _quest.rewardTokenId > 0, "INCORRECT_ANSWER");
        require(!_quest.solved, "QUEST_ALREADY_SOLVED");

        /// @dev set the quest as solved
        quests[_answer].solved = true;
        questData[quests[_answer].number].quest.solved = true;

        /// @dev send ERC20 tokens to user
        if (_quest.rewardAmount > 0) {
            _quest.rewardToken.transfer(msg.sender, _quest.rewardAmount);
        }

        /// @dev send ERC721 token to user
        if (_quest.rewardTokenId > 0) {
            _quest.rewardNft.transferFrom(address(this), msg.sender, _quest.rewardTokenId);
        }
    }

    /**
     *  @notice Add a new quest with its rewards
     *  @param _answer quest's correct answer
     *  @param _quest quest data
     */
    function addQuest(bytes32 _answer, Quest memory _quest) external onlyOwner {
        require(quests[_answer].rewardAmount == 0 && quests[_answer].rewardTokenId == 0, "QUEST_ALREADY_EXISTS");

        quests[_answer] = _quest;
        questData.push(QuestView({ quest: _quest, answer: _answer }));
    }

    /**
     *  @notice Remove a quest
     *  @param _answer quest's correct answer
     */
    function removeQuest(bytes32 _answer) external onlyOwner {
        if (quests[_answer].rewardAmount > 0) {
            quests[_answer].rewardToken.transfer(msg.sender, quests[_answer].rewardAmount);
        }

        if (quests[_answer].rewardTokenId > 0) {
            quests[_answer].rewardNft.transferFrom(address(this), msg.sender, quests[_answer].rewardTokenId);
        }

        delete questData[quests[_answer].number];
        delete quests[_answer];
    }

    function getQuestData() external view returns (QuestView[] memory) {
        return questData;
    }
}