contract StrategyCurve3CrvV1 is StrategyBase {
    // Curve stuff
    address public want = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490;
    address public gauge = 0xbFcF63294aD7105dEa65aA58F8AE5BE2D9d0952A;
    // 3pool
    address public curve = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
    address public mintr = 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0;

    // stablecoins
    address public dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address public usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    address public usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
    address public susd = 0x57Ab1ec28D129707052df4dF418D58a2D46d5f51;

    // bitcoins
    address public wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
    address public renbtc = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D;

    // rewards
    address public crv = 0xD533a949740bb3306d119CC777fa900bA034cd52;

    constructor(
        address _btf,
        address _governance,
        address _strategist,
        address _controller,
        address _timelock
    )
    public
    StrategyBase(_btf, want, _governance, _strategist, _controller, _timelock)
    {
    }

    // **** Views ****

    function getMostPremium()
    public
    view
    returns (address, uint256)
    {
        uint256[] memory balances = new uint256[](3);
        // DAI
        balances[0] = ICurveFi_3(curve).balances(0);
        // USDC
        balances[1] = ICurveFi_3(curve).balances(1).mul(10 ** 12);
        // USDT
        balances[2] = ICurveFi_3(curve).balances(2).mul(10 ** 12);

        // DAI
        if (
            balances[0] < balances[1] &&
            balances[0] < balances[2]
        ) {
            return (dai, 0);
        }

        // USDC
        if (
            balances[1] < balances[0] &&
            balances[1] < balances[2]
        ) {
            return (usdc, 1);
        }

        // USDT
        if (
            balances[2] < balances[0] &&
            balances[2] < balances[1]
        ) {
            return (usdt, 2);
        }

        // If they're somehow equal, we just want DAI
        return (dai, 0);
    }

    function getName() external override pure returns (string memory) {
        return "StrategyCurve3CrvV1";
    }

    // **** Getters ****

    function balanceOfPool() public override view returns (uint256) {
        return ICurveGauge(gauge).balanceOf(address(this));
    }

    function getHarvestable() external returns (uint256) {
        return ICurveGauge(gauge).claimable_tokens(address(this));
    }

    // **** State Mutation functions ****

    function harvest() public override onlyBenevolent {
        // Anyone can harvest it at any given time.
        // I understand the possibility of being frontrun
        // But ETH is a dark forest, and I wanna see how this plays out
        // i.e. will be be heavily frontrunned?
        //      if so, a new strategy will be deployed.

        // stablecoin we want to convert to
        (address to, uint256 toIndex) = getMostPremium();

        // Collects crv tokens
        // Don't bother voting in v1
        ICurveMintr(mintr).mint(gauge);
        uint256 _crv = IERC20(crv).balanceOf(address(this));
        if (_crv > 0) {
            _swapUniswap(crv, to, _crv);
        }

        // Adds liquidity to curve.fi's pool
        uint256 _to = IERC20(to).balanceOf(address(this));
        if (_to > 0) {
            if (devFundFee > 0) {
                uint256 _devFundFee = _to.mul(devFundFee).div(devFundMax);
                IERC20(to).transfer(
                    IController(controller).devAddr(),
                    _devFundFee
                );
            }

            // Burn some btfs first
            if (burnFee > 0) {
                uint256 _burnFee = _to.mul(burnFee).div(burnMax);
                _swapUniswap(to, btf, _burnFee);
                IERC20(btf).transfer(
                    IController(controller).burnAddr(),
                    IERC20(btf).balanceOf(address(this))
                );
            }

            if (comFundFee > 0) {
                uint256 _comFundFee = _to.mul(comFundFee).div(comFundMax);
                IERC20(to).transfer(
                    IController(controller).comAddr(),
                    _comFundFee
                );
            }

            _to = IERC20(to).balanceOf(address(this));
            IERC20(to).safeApprove(curve, 0);
            IERC20(to).safeApprove(curve, _to);
            uint256[3] memory liquidity;
            liquidity[toIndex] = _to;
            ICurveFi_3(curve).add_liquidity(liquidity, 0);
        }

        // get back want (3crv)
        uint256 _want = IERC20(want).balanceOf(address(this));
        if (_want > 0) {
            deposit();
        }
    }

    function deposit() public override {
        uint256 _want = IERC20(want).balanceOf(address(this));
        if (_want > 0) {
            IERC20(want).safeApprove(gauge, 0);
            IERC20(want).safeApprove(gauge, _want);
            ICurveGauge(gauge).deposit(_want);
        }
    }

    function _withdrawSome(uint256 _amount)
    internal
    override
    returns (uint256)
    {
        ICurveGauge(gauge).withdraw(_amount);
        return _amount;
    }
}
function getMostPremium()
public
view
returns (address, uint256)
{
    uint256[] memory balances = new uint256[](3);
    // DAI
    balances[0] = ICurveFi_3(curve).balances(0);
    // USDC
    balances[1] = ICurveFi_3(curve).balances(1).mul(10 ** 12);
    // USDT
    balances[2] = ICurveFi_3(curve).balances(2).mul(10 ** 12);

    // DAI
    if (
        balances[0] < balances[1] &&
        balances[0] < balances[2]
    ) {
        return (dai, 0);
    }

    // USDC
    if (
        balances[1] < balances[0] &&
        balances[1] < balances[2]
    ) {
        return (usdc, 1);
    }

    // USDT
    if (
        balances[2] < balances[0] &&
        balances[2] < balances[1]
    ) {
        return (usdt, 2);
    }

    // If they're somehow equal, we just want DAI
    return (dai, 0);
}

// **** Views ****