// File: src/ConfigManager.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**

    _____________
   /  ::  K  ::  \
   [] ::  A  :: []
   [] ::::R:::: []
   [] ::::R:::: []
   [] ::::O:::: []
   [] ::::T:::: []
   [_____________]
       I     I
       I_   _I
        /   \
        \   /
        (   )   
        /   \
        \___/

   ____     ____        __      _   _________    _____      _____   
  / ___)   / __ \      /  \    / ) (_   _____)  (_   _)    / ___ \  
 / /      / /  \ \    / /\ \  / /    ) (___       | |     / /   \_) 
( (      ( ()  () )   ) ) ) ) ) )   (   ___)      | |    ( (  ____  
( (      ( ()  () )  ( ( ( ( ( (     ) (          | |    ( ( (__  ) 
 \ \___   \ \__/ /   / /  \ \/ /    (   )        _| |__   \ \__/ /  
  \____)   \____/   (_/    \__/      \_/        /_____(    \____/   
                                                                

 */

import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IAttackRewardCalculator.sol";
import "./interfaces/IKarrotsToken.sol";
import "./interfaces/IRabbit.sol";
import "./interfaces/IStolenPool.sol";
import "./interfaces/IDexInterfacer.sol";
import "./interfaces/IKarrotChef.sol";
import "./interfaces/IFullProtec.sol";

/**
Manager for config variables

Notes:
- once operation is stable, timelock will be set as owner
- openKarrotChefDeposits: first stolen pool epoch (epoch 0) starts at the same time as the karrot chef deposits open
 */


contract ConfigManager is Ownable {
    //======================================================================================
    // setup
    //======================================================================================

    IRabbit public rabbit;
    IKarrotsToken public karrots;
    IStolenPool public stolenPool;
    IAttackRewardCalculator public rewardCalculator;
    IDexInterfacer public dexInterfacer;
    IKarrotChef public karrotChef;
    IFullProtec public karrotFullProtec;

    address public treasuryAddress; 
    address public uniswapFactoryAddress; 
    address public uniswapRouterAddress; 
    address public karrotsPoolAddress;
    address public timelockControllerAddress;
    address public karrotsAddress;
    address public karrotChefAddress;
    address public karrotFullProtecAddress;
    address public karrotStolenPoolAddress;
    address public rabbitAddress;
    address public randomizerAddress; 
    address public attackRewardCalculatorAddress;
    address public dexInterfacerAddress;
    address public presaleDistributorAddress; 
    address public airdropDistributorAddress;

    constructor(
        address _treasuryAddress,
        address _uniswapFactoryAddress,
        address _uniswapRouterAddress,
        address _randomizerAddress,
        address _presaleDistributorAddress,
        address _airdropDistributorAddress
    ) {
        treasuryAddress = _treasuryAddress;
        uniswapFactoryAddress = _uniswapFactoryAddress;
        uniswapRouterAddress = _uniswapRouterAddress;
        randomizerAddress = _randomizerAddress;
        presaleDistributorAddress = _presaleDistributorAddress;
        airdropDistributorAddress = _airdropDistributorAddress;
    }

    function transferOwnershipToTimelock() external onlyOwner {
        transferOwnership(timelockControllerAddress);
    }

    //======================================================================================
    // include setters for each "global" parameter --> gated by onlyOwner
    //======================================================================================

    function setTreasuryAddress(address _treasuryAddress) external onlyOwner {
        treasuryAddress = _treasuryAddress;
    }

    function setUniswapFactoryAddress(address _uniswapFactoryAddress) external onlyOwner {
        uniswapFactoryAddress = _uniswapFactoryAddress;        
    }

    function setUniswapRouterAddress(address _uniswapRouterAddress) external onlyOwner {
        uniswapRouterAddress = _uniswapRouterAddress;        
    }

    function setKarrotsPoolAddress(address _karrotsPoolAddress) external onlyOwner {
        karrotsPoolAddress = _karrotsPoolAddress;        
    }

    function setTimelockControllerAddress(address _timelockControllerAddress) external onlyOwner {
        timelockControllerAddress = _timelockControllerAddress;       
    }

    function setKarrotTokenAddress(address _karrotTokenAddress) external onlyOwner {
        karrotsAddress = _karrotTokenAddress;
        karrots = IKarrotsToken(_karrotTokenAddress);    
    }

    function setKarrotChefAddress(address _karrotChefAddress) external onlyOwner {
        karrotChefAddress = _karrotChefAddress;
        karrotChef = IKarrotChef(_karrotChefAddress);       
    }

    function setKarrotFullProtecAddress(address _fullProtecAddress) external onlyOwner {
        karrotFullProtecAddress = _fullProtecAddress;
        karrotFullProtec = IFullProtec(_fullProtecAddress);      
    }

    function setKarrotStolenPoolAddress(address _stolenPoolAddress) external onlyOwner {
        karrotStolenPoolAddress = _stolenPoolAddress;
        stolenPool = IStolenPool(_stolenPoolAddress);   
    }

    function setRabbitAddress(address _rabbitAddress) external onlyOwner {
        rabbitAddress = _rabbitAddress;
        rabbit = IRabbit(_rabbitAddress);       
    }

    function setRandomizerAddress(address _randomizerRequesterAddress) external onlyOwner {
        randomizerAddress = _randomizerRequesterAddress;        
    }

    function setRewardCalculatorAddress(address _rewardCalculatorAddress) external onlyOwner {
        attackRewardCalculatorAddress = _rewardCalculatorAddress;
        rewardCalculator = IAttackRewardCalculator(_rewardCalculatorAddress);       
    }

    function setDexInterfacerAddress(address _dexInterfacerAddress) external onlyOwner {
        dexInterfacerAddress = _dexInterfacerAddress;
        dexInterfacer = IDexInterfacer(_dexInterfacerAddress);      
    }

    function setPresaleDistributorAddress(address _presaleClaimContractAddress) external onlyOwner {
        presaleDistributorAddress = _presaleClaimContractAddress;      
    }

    //======================================================================================
    // NEW SETTERS FOR KARROTS CONFIG (CALLS FUNCTIONS ON KARROTS)
    //======================================================================================

    function setBuyTaxRate(uint16 _buyTaxRate) external onlyOwner {
        karrots.setBuyTaxRate(_buyTaxRate);
    }

    function setSellTaxRate(uint16 _sellTaxRate) external onlyOwner {
        karrots.setSellTaxRate(_sellTaxRate);
    }

    function addDexAddress(address _dexAddress) external onlyOwner {
        karrots.addDexAddress(_dexAddress);
    }

    function removeDexAddress(address _dexAddress) external onlyOwner {
        karrots.removeDexAddress(_dexAddress);
    }

    function setMaxScaleFactorDecreasePercentagePerDebase(uint256 _maxScaleFactorDecreasePercentagePerDebase) external onlyOwner {
        karrots.setMaxScaleFactorDecreasePercentagePerDebase(_maxScaleFactorDecreasePercentagePerDebase);
    }

    function setTaxSwapAmountThreshold(uint256 _taxSwapAmountThreshold) external onlyOwner {
        karrots.setTaxSwapAmountThreshold(_taxSwapAmountThreshold);
    }

    function setDivertTaxToStolenPoolRate(uint256 _divertRate) external onlyOwner {
        karrots.setDivertTaxToStolenPoolRate(_divertRate);
    }

    //======================================================================================
    // NEW SETTERS FOR RABBIT (CALLS FUNCTIONS ON RABBIT)
    //======================================================================================

    function setRabbitMintIsOpen(bool _rabbitMintIsOpen) external onlyOwner {
        rabbit.setRabbitMintIsOpen(_rabbitMintIsOpen);
    }

    function setRabbitBatchSize(uint16 _rabbitBatchSize) external onlyOwner {
        rabbit.setRabbitBatchSize(_rabbitBatchSize);
    }

    function setRabbitMintSecondsBetweenBatches(uint32 _rabbitMintSecondsBetweenBatches) external onlyOwner {
        rabbit.setRabbitMintSecondsBetweenBatches(_rabbitMintSecondsBetweenBatches);
    }

    function setRabbitMaxPerWallet(uint8 _rabbitMaxPerWallet) external onlyOwner {
        rabbit.setRabbitMaxPerWallet(_rabbitMaxPerWallet);
    }

    function setRabbitMintPriceInKarrots(uint128 _rabbitMintPriceInKarrots) external onlyOwner {
        rabbit.setRabbitMintPriceInKarrots(_rabbitMintPriceInKarrots);
    }

    function setRabbitMintKarrotFeePercentageToBurn(uint16 _rabbitMintKarrotFeePercentageToBurn) external onlyOwner {
        rabbit.setRabbitMintKarrotFeePercentageToBurn(_rabbitMintKarrotFeePercentageToBurn);
    }

    function setRabbitMintKarrotFeePercentageToStolenPool(uint16 _rabbitMintKarrotFeePercentageToStolenPool) external onlyOwner {
        rabbit.setRabbitMintKarrotFeePercentageToStolenPool(_rabbitMintKarrotFeePercentageToStolenPool);
    }

    function setRabbitMintTier1Threshold(uint16 _rabbitMintTier1Threshold) external onlyOwner {
        rabbit.setRabbitMintTier1Threshold(_rabbitMintTier1Threshold);
    }

    function setRabbitMintTier2Threshold(uint16 _rabbitMintTier2Threshold) external onlyOwner {
        rabbit.setRabbitMintTier2Threshold(_rabbitMintTier2Threshold);
    }

    function setRabbitHP(uint8 _rabbitHP) external onlyOwner {
        rabbit.setRabbitHP(_rabbitHP);
    }

    function setRabbitHitRate(uint16 _rabbitHitRate) external onlyOwner {
        rabbit.setRabbitHitRate(_rabbitHitRate);
    }

    function setRabbitAttackIsOpen(bool _isOpen) external onlyOwner {
        stolenPool.setStolenPoolAttackIsOpen(_isOpen);
        rabbit.setRabbitAttackIsOpen(_isOpen);
    }

    function setAttackCooldownSeconds(uint32 _attackCooldownSeconds) external onlyOwner {
        rabbit.setAttackCooldownSeconds(_attackCooldownSeconds);
    }

    function setAttackHPDeductionAmount(uint8 _attackHPDeductionAmount) external onlyOwner {
        rabbit.setAttackHPDeductionAmount(_attackHPDeductionAmount);
    }

    function setAttackHPDeductionThreshold(uint16 _attackHPDeductionThreshold) external onlyOwner {
        rabbit.setAttackHPDeductionThreshold(_attackHPDeductionThreshold);
    }


    //======================================================================================
    // NEW SETTERS FOR KARROT CHEF (CALLS FUNCTIONS ON KARROT CHEF)
    //======================================================================================

    function setKarrotChefPoolAllocPoints(uint256 _pid, uint128 _allocPoints, bool _withUpdate) external onlyOwner {
        karrotChef.setAllocationPoint(_pid, _allocPoints, _withUpdate);
    }

    function setKarrotChefLockDuration(uint256 _pid, uint256 _lockDuration) external onlyOwner {
        karrotChef.setLockDuration(_pid, _lockDuration);
    }

    function updateKarrotChefRewardPerBlock(uint88 _karrotChefRewardPerBlock) external onlyOwner {
        karrotChef.updateRewardPerBlock(_karrotChefRewardPerBlock);
    }

    function setKarrotChefDebaseMultiplier(uint48 _debaseMultiplier) external onlyOwner {
        karrotChef.setDebaseMultiplier(_debaseMultiplier);
    }

    function openKarrotChefDeposits() external onlyOwner {
        karrotChef.openKarrotChefDeposits();
        stolenPool.setStolenPoolOpenTimestamp();
    }

    function setDepositIsPaused(bool _depositIsPaused) external onlyOwner {
        karrotChef.setDepositIsPaused(_depositIsPaused);
    }

    function setClaimTaxRate(uint16 _maxClaimTaxRate) external onlyOwner {
        karrotChef.setClaimTaxRate(_maxClaimTaxRate);
    }

    function setFullProtecLiquidityProportion(uint16 _fullProtecLiquidityProportion) external onlyOwner {
        karrotChef.setFullProtecLiquidityProportion(_fullProtecLiquidityProportion);
    }

    //======================================================================================
    // NEW SETTERS FOR FULL PROTEC
    //======================================================================================

    function openFullProtecDeposits() external onlyOwner {
        karrotFullProtec.openFullProtecDeposits();
    }

    function setFullProtecLockDuration(uint32 _lockDuration) external onlyOwner {
        karrotFullProtec.setFullProtecLockDuration(_lockDuration);
    }

    function setThresholdFullProtecKarrotBalance(uint224 _thresholdFullProtecKarrotBalance) external onlyOwner {
        karrotFullProtec.setThresholdFullProtecKarrotBalance(_thresholdFullProtecKarrotBalance);
    }

    //======================================================================================
    // NEW SETTERS FOR STOLEN POOL 
    //======================================================================================

    function setAttackBurnPercentage(uint16 _attackBurnPercentage) external onlyOwner {
        stolenPool.setAttackBurnPercentage(_attackBurnPercentage);
    }

    function setIsApprovedStolenPoolDepositor(address _depositor, bool _isApproved) external onlyOwner {
        stolenPool.setIsApprovedDepositor(_depositor, _isApproved);
    }
}

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



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling 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: src/interfaces/IAttackRewardCalculator.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IAttackRewardCalculator {
    function calculateRewardPerAttackByTier(
        uint256 tier1Attacks,
        uint256 tier2Attacks,
        uint256 tier3Attacks,
        uint256 tier1Weight,
        uint256 tier2Weight,
        uint256 tier3Weight,
        uint256 totalKarrotsDepositedThisEpoch
    ) external view returns (uint256[] memory);
}
// File: src/interfaces/IKarrotsToken.sol



//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IKarrotsToken {
    function approve(address spender, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function addDexAddress(address _dexAddress) external;
    function removeDexAddress(address _dexAddress) external;
    function mint(address to, uint256 amount) external;
    function burn(uint256 amount) external;
    function burnFrom(address account, uint256 amount) external;
    function rebase(uint256 epoch, uint256 indexDelta, bool positive) external returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function transferUnderlying(address to, uint256 value) external returns (bool);
    function fragmentToKarrots(uint256 value) external view returns (uint256);
    function karrotsToFragment(uint256 karrots) external view returns (uint256);
    function balanceOfUnderlying(address who) external view returns (uint256);
    function setSellTaxRate(uint16 _sellTaxRate) external;
    function setBuyTaxRate(uint16 _buyTaxRate) external;
    function setMaxScaleFactorDecreasePercentagePerDebase(uint256 _maxScaleFactorDecreasePercentagePerDebase) external;
    function setTaxSwapAmountThreshold(uint256 _taxSwapAmountThreshold) external;
    function setDivertTaxToStolenPoolRate(uint256 _divertRate) external;
}
// File: src/interfaces/IRabbit.sol



//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IRabbit {
    function getRabbitSupply() external view returns (uint256);
    function getRabbitIdsByOwner(address _owner) external view returns (uint256[] memory);
    function setRabbitMintIsOpen(bool _isOpen) external;
    function setRabbitBatchSize(uint16 _batchSize) external;
    function setRabbitMintSecondsBetweenBatches(uint32 _secondsBetweenBatches) external;
    function setRabbitMaxPerWallet(uint8 _maxPerWallet) external;
    function setRabbitMintPriceInKarrots(uint128 _priceInKarrots) external;
    function setRabbitMintKarrotFeePercentageToBurn(uint16 _rabbitMintKarrotFeePercentageToBurn) external;
    function setRabbitMintKarrotFeePercentageToStolenPool(uint16 _rabbitMintKarrotFeePercentageToStolenPool) external;
    function setRabbitMintTier1Threshold(uint16 _tier1Threshold) external;
    function setRabbitMintTier2Threshold(uint16 _tier2Threshold) external;
    function setRabbitHP(uint8 _HP) external;
    function setRabbitHitRate(uint16 _hitRate) external;
    function setRabbitAttackIsOpen(bool _isOpen) external;
    function setAttackCooldownSeconds(uint32 _attackCooldownSeconds) external;
    function setAttackHPDeductionAmount(uint8 _attackHPDeductionAmount) external;
    function setAttackHPDeductionThreshold(uint16 _attackHPDeductionThreshold) external;
}
// File: src/interfaces/IStolenPool.sol



//SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IStolenPool {
    function virtualDeposit(uint256 _amount) external;
    function attack(address _sender, uint256 _rabbitTier, uint256 _rabbitId) external;
    function updateConfig() external;
    function setStolenPoolOpenTimestamp() external;
    function setStolenPoolAttackIsOpen(bool _isOpen) external;
    function setAttackBurnPercentage(uint16 _attackBurnPercentage) external;
    function setIsApprovedDepositor(address _depositor, bool _isApproved) external;
}
// File: src/interfaces/IDexInterfacer.sol



//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IDexInterfacer {
    function updateConfig() external;
    function depositEth() external payable;
    function depositErc20(uint256 _amount) external;
    function getPoolIsCreated() external view returns (bool);
    function getPoolIsFunded() external view returns (bool);
}
// File: src/interfaces/IKarrotChef.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IKarrotChef {
    function deposit(uint256 _pid, uint256 _amount) external;
    function withdraw(uint256 _pid, uint256 _amount) external;
    function claim(uint256 _pid) external;
    function attack() external;
    function getUserStakedAmount(address _user) external view returns (uint256);
    function getTotalStakedAmount() external view returns (uint256);
    function setAllocationPoint(uint256 _pid, uint128 _allocPoint, bool _withUpdatePools) external;
    function setLockDuration(uint256 _pid, uint256 _lockDuration) external;
    function updateRewardPerBlock(uint88 _rewardPerBlock) external;
    function setDebaseMultiplier(uint48 _debaseMultiplier) external;
    function openKarrotChefDeposits() external;
    function setDepositIsPaused(bool _isPaused) external;
    function setThresholdFullProtecKarrotBalance(uint256 _thresholdFullProtecKarrotBalance) external;
    function setClaimTaxRate(uint16 _maxTaxRate) external;
    function setFullProtecLiquidityProportion(uint16 _fullProtecLiquidityProportion) external;
    function getFullToChefRatio(address _user) external view returns (uint256);
}
// File: src/interfaces/IFullProtec.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IFullProtec {
    function getUserStakedAmount(address _user) external view returns (uint256);
    function getTotalStakedAmount() external view returns (uint256);
    function getIsUserAboveThresholdToAvoidClaimTax(address _user) external view returns (bool);
    function updateConfig() external;
    function openFullProtecDeposits() external;
    function setFullProtecLockDuration(uint32 _lockDuration) external;
    function setThresholdFullProtecKarrotBalance(uint224 _thresholdFullProtecKarrotBalance) external;
}
// 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;
    }
}