// Contract: DiamondHandTokenWrapper
// Target function: function buyBack (address managerAddr, uint256 ethForBuyback) external override onlyToken returns (uint256)
//SPDX-License-Identifier: Unlicensed

pragma solidity ^0.8.0;


interface IERC20 {
  function transfer(address to, uint256 value) external returns (bool);
  function approve(address spender, uint256 value) external returns (bool);
  function transferFrom(address from, address to, uint256 value) external returns (bool);
  function totalSupply() external view returns (uint256);
  function balanceOf(address account) external view returns (uint256);
  function allowance(address owner, address spender) external view returns (uint256);
  function decimals() external view returns (uint8);
  function burn(uint256 amount) external;
  event Transfer(address indexed from, address indexed to, uint256 value);
  event Approval(address indexed owner, address indexed spender, uint256 value);
}


library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;

        return c;
    }

    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}


interface IPancakeRouter01 {
    function factory () external pure returns (address);
    function factoryV1 () external pure returns (address);
    function factoryV2 () external pure returns (address);
    function WETH () external pure returns (address);
    function WPLS () external pure returns (address);
    function WAVAX () external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);

    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);

    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);

    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);

    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);

    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);

    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

pragma solidity >=0.6.2;

interface IRouter is IPancakeRouter01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

pragma solidity >=0.6.2;

interface IFactory {
function getPair (address token1, address token2) external pure returns (address);
}


contract DiamondHandTokenWrapper {
    using SafeMath for uint256;


    address public bridgeToken = 0xB8482D0f2119B2309071dc9C154BFf2fF9900B21;  // ON ETH
    address public functETHtoken = 0xf5f2C77193dFc12202C1aE396aCB9e25B00Afafa; // real test DT on ETH

    address public _dev = 0x60859bAF5f538c5F82219e286E73Df1DE43ceEde;


    event Wrapping (address indexed user, uint256 tokenAmount, string action);    


    modifier onlyDev() {
        require(msg.sender == _dev); _;
    }


    function setAddresses (address _bridgeToken, address _functETHtoken) public onlyDev { 
            require(bridgeToken == 0xB8482D0f2119B2309071dc9C154BFf2fF9900B21, "setAddresses: Addresses already set!");
            require(functETHtoken == 0xf5f2C77193dFc12202C1aE396aCB9e25B00Afafa, "setAddresses: Addresses already set!");
            bridgeToken = _bridgeToken;
            functETHtoken = _functETHtoken;
    }


    function getYourBridgeTokenBal () public view returns (uint256) {
        uint256 userBalance = IERC20(bridgeToken).balanceOf(msg.sender);
        return userBalance;
    }


    function getYourRealTokenBal () public view returns (uint256) {
        uint256 userBalance = IERC20(functETHtoken).balanceOf(msg.sender);
        return userBalance;
    }



    function deposit (uint256 amount) public {
        uint256 userBalance = IERC20(bridgeToken).balanceOf(msg.sender);
        require (userBalance >= amount, "Wrapper Deposit: The amount you want to wrap exceeds your balance!");

        uint256 wrapperBalance = IERC20(functETHtoken).balanceOf(address(this));
        require (wrapperBalance >= amount, "Wrapper Deposit: The amount you want to wrap exceeds the contract balance!");

        IERC20(bridgeToken).transferFrom(msg.sender, address(this), amount);

        IERC20(functETHtoken).transfer(msg.sender, amount);

        emit Wrapping(msg.sender, amount, "deposit/wrap");
    }


    function withdraw (uint256 amount) public {
        uint256 userBalance = IERC20(functETHtoken).balanceOf(msg.sender);
        require (userBalance >= amount, "Wrapper Withdraw: The amount you want to unwrap exceeds your balance!");

        uint256 wrapperBalance = IERC20(bridgeToken).balanceOf(address(this));
        require (wrapperBalance >= amount, "Wrapper Withdraw: The amount you want to unwrap exceeds the contract balance!");

        IERC20(functETHtoken).transferFrom(msg.sender, address(this), amount);

        IERC20(bridgeToken).transfer(msg.sender, amount);

        emit Wrapping(msg.sender, amount, "withdraw/unwrap");
    }


}


interface ISuperTokenManager {
    function burnLP(uint8 buyBackPercent, address tradingPair, address managerAddr, uint256 lpBal ) external returns (uint256, uint256);
    function buyBack(address managerAddr, uint256 ethForBuyback ) external returns (uint256);
}


contract SuperTokenManager is ISuperTokenManager {
    using SafeMath for uint256;

    address public _token;
    IRouter public _router;

    address payable public _dev;

    uint256 public collectedToken;
    uint256 public totalBurnedToken;

    modifier onlyToken() {
        require(msg.sender == _token); _;
    }

    constructor (address router, address payable dev) {
        _router = IRouter(router);
        _token = msg.sender;
        _dev = dev;
    }

 
    // to make the contract being able to receive ETH from Router
    receive() external payable {}


    // function to remove some of the collected LP (onlyToken!)
    function burnLP (uint8 buyBackPercent, address tradingPair, address managerAddr, uint256 lpBal ) external override onlyToken returns (uint256, uint256) {   

        if (IERC20(tradingPair).allowance(managerAddr, address(_router)) == 0) {
         IERC20(tradingPair).approve(address(_router), type(uint256).max);
        }

        uint256 tokenBalBefore = IERC20(_token).balanceOf(managerAddr);

        uint256 lpBalToRemove = lpBal.mul(buyBackPercent).div(1000);

        // remove X% of the colected Liq daily to buyback Token
        (uint256 ethGain, uint256 tokenGain) =
        _router.removeLiquidity(
          _router.WETH(),
          _token,
          lpBalToRemove,
          0,
          0,
          managerAddr,
          block.timestamp + 100
        );

        collectedToken = (IERC20(_token).balanceOf(managerAddr)).sub(tokenBalBefore);

        IERC20(_router.WETH()).transfer(address(_token), ethGain);

        return ( ethGain, tokenGain);
    }