contract StrategyTRUTusdV1 is StrategyBase {
    //want token TUSD
    address public constant tusd = 0x0000000000085d4780B73119b644AE5ecd22b376;

    // staking constants
    address public constant trueFiPool = 0xa1e72267084192Db7387c8CC1328fadE470e4149;
    address public constant stakingFarm = 0x8FD832757F58F71BAC53196270A4a55c8E1a29D9;
    address public constant rewardToken = 0x4C19596f5aAfF459fA38B0f7eD92F11AE6543784; //TRU

    // https://etherscan.io/address/0xda55fcf03af0a6d954ac06b995c8908076506b3a#code#L1878
    uint256 public maxSlip = 9990;

    constructor(
        address _governance,
        address _strategist,
        address _controller,
        address _timelock
    )
        public StrategyBase(tusd, _governance, _strategist, _controller, _timelock)
    {		        
        IERC20(tusd).safeApprove(trueFiPool, uint256(-1));
        IERC20(trueFiPool).safeApprove(stakingFarm, uint256(-1));
    }

    //setter
    function setMaxSlip(uint256 _maxSlip) public onlyBenevolent{
        maxSlip = _maxSlip;
    }
	
    // **** State Mutations ****
	
    function deposit() public override {
        uint256 _wantAmt = IERC20(want).balanceOf(address(this));
        require(_wantAmt > 0, "_wantAmt");

        TrueFiPool(trueFiPool).join(_wantAmt);
        uint256 _lpAmount = IERC20(trueFiPool).balanceOf(address(this));

        require(_lpAmount > 0, "!_lpAmount");
        TrueFarm(stakingFarm).stake(_lpAmount);
    }
	
    function _withdrawSome(uint256 _amount) internal override returns (uint256) {
        if (_amount == 0){
            return 0;
        }

        uint256 _poolSlip = TrueFiPool(trueFiPool).liquidExitPenalty(_amount);
        require(_poolSlip >= maxSlip, "!maxSlip");

        uint256 _beforeWant = IERC20(want).balanceOf(address(this));

        uint256 poolValue = TrueFiPool(trueFiPool).poolValue();
        uint256 totalSupply = TrueFiPool(trueFiPool).totalSupply();
        uint256 _withdrawAmt = _amount.mul(totalSupply).div(poolValue);

        uint256 _totalStaked = TrueFarm(stakingFarm).staked(address(this));

        _withdrawAmt  = _withdrawAmt > _totalStaked ? _totalStaked : _withdrawAmt;

        TrueFarm(stakingFarm).unstake(_withdrawAmt);
        uint256 _lpAmount = IERC20(trueFiPool).balanceOf(address(this));

        TrueFiPool(trueFiPool).liquidExit(_lpAmount);

        uint256 _afterWant = IERC20(want).balanceOf(address(this));

        return _afterWant.sub(_beforeWant);
    }

    // Controller only function for creating additional rewards from dust
    function withdraw(IERC20 _asset) external override returns (uint256 balance) {
        require(address(_asset) != want, 'wantToken');
        require(address(_asset) != trueFiPool, 'tfToken');
        require(address(_asset) != stakingFarm, 'tfiLPFarming');
        balance = _withdrawNonWantAsset(_asset);
    }
	
    function _convertWantToBuyback(uint256 _lpAmount) internal override returns (address, uint256){
        require(_lpAmount > 0, '!_lpAmount');
        return (want, _lpAmount);
    }

    function harvest() public override onlyBenevolent {

        // Collects reward(TRU) tokens
        TrueFarm(stakingFarm).claim();
		
        uint256 _reward = IERC20(rewardToken).balanceOf(address(this));

        if (_reward > 0) {
            _swapUniswap(rewardToken, want, _reward); // TRU -> TUSD
            uint256 _wantAmount = IERC20(want).balanceOf(address(this));

            uint256 _buybackLpAmount = _wantAmount.mul(performanceFee).div(performanceMax);
            if (buybackEnabled == true && _buybackLpAmount > 0){
                (address _buybackPrinciple, uint256 _buybackAmount) = _convertWantToBuyback(_buybackLpAmount);
                buybackAndNotify(_buybackPrinciple, _buybackAmount);
            }

            lastHarvestBlock = block.number;
            lastHarvestInWant = IERC20(want).balanceOf(address(this));
            deposit();
        }
    }

    // **** Views ****
    function balanceOfPool() public override view returns (uint256){
        uint256 _lpAmount = TrueFarm(stakingFarm).staked(address(this));
        uint256 poolValue = TrueFiPool(trueFiPool).poolValue();
        uint256 totalSupply = TrueFiPool(trueFiPool).totalSupply();

        return _lpAmount.mul(poolValue).div(totalSupply);
    }

    function getHarvestable() external view returns (uint256) {
        return TrueFarm(stakingFarm).claimable(address(this)); //TRU rewards
    }
	
    function getName() external override pure returns (string memory) {
        return "StrategyTRUTusdV1";
    }
}
function balanceOfPool() public override view returns (uint256){
    uint256 _lpAmount = TrueFarm(stakingFarm).staked(address(this));
    uint256 poolValue = TrueFiPool(trueFiPool).poolValue();
    uint256 totalSupply = TrueFiPool(trueFiPool).totalSupply();

    return _lpAmount.mul(poolValue).div(totalSupply);
}

// **** Views ****