// Contract: GRAVYToken
// Target function: function delegateBySig(
.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

interface Callable {
    function tokenCallback(address _from, uint256 _tokens, bytes calldata _data) external returns (bool);
    function receiveApproval(address _from, uint256 _tokens, address _token, bytes calldata _data) external;
}

contract GRAVYToken is Context, IERC20, Ownable {
    using SafeMath for uint256;
    using Address for address;
    
    event LogBurn(uint256 decayrate, uint256 totalSupply);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    modifier validRecipient(address to) {
        require(to != address(0x0));
        require(to != address(this));
        _;
    }

    string public constant _name = "gravy";
    string public constant _symbol = "GRVY";
    uint8 public _decimals = 18;
    
    uint256 private constant DECIMALS = 18;
    uint256 private constant MAX_UINT256 = ~uint256(0); //(2^256) - 1
    uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 20000 * 10**DECIMALS;
    uint256 private constant TOTAL_GONS = MAX_UINT256 - (MAX_UINT256 % INITIAL_FRAGMENTS_SUPPLY);
    uint256 private constant MAX_SUPPLY = ~uint128(0); //(2^128) - 1
    
    uint256 private constant max_supply = 100000 * 10**DECIMALS;
    bool public maxSupplyHit = false;
    
    // The MasterChef contract
    address public masterchefAddress;
    // The Uniswap GRAVY-ETH LP token address
    address public gravyPoolAddress;
   
    uint256 private _totalSupply;
    uint256 private _gonsPerFragment;
    mapping(address => uint256) private _gonBalances;
    mapping (address => mapping (address => uint256)) private _allowedFragments;
   
    uint256 public transBurnrate = 300;//3%
    
    uint256 public decayBurnrate = 325;
    
    uint256 public constant maxtransBurnrate = 500;
    
    uint256 public constant maxdecayBurnrate = 500;
    
    
    // @notice A record of each accounts delegate
    mapping (address => address) internal _delegates;
    // @notice A checkpoint for marking number of votes from a given block
    struct Checkpoint {
        uint32 fromBlock;
        uint256 votes;
    }
    // @notice A record of votes checkpoints for each account, by index
    mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
    // @notice The number of checkpoints for each account
    mapping (address => uint32) public numCheckpoints;
    // @notice The EIP-712 typehash for the contract's domain
    bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
    // @notice The EIP-712 typehash for the delegation struct used by the contract
    bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
    // @notice A record of states for signing / validating signatures
    mapping (address => uint) public nonces;
    // @notice An event thats emitted when an account changes its delegate
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
    // @notice An event thats emitted when a delegate account's vote balance changes
    event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);

   

    constructor() public {
        _owner = msg.sender;
        
        _totalSupply = INITIAL_FRAGMENTS_SUPPLY;
        _gonBalances[_owner] = TOTAL_GONS;
        _gonsPerFragment = TOTAL_GONS.div(_totalSupply);

        emit Transfer(address(0x0), _owner, _totalSupply);
    }
    
    function burn() public onlyOwner {
        uint256 _remainrate = 10000; //0.25%->decayrate=25
        _remainrate = _remainrate.sub(decayBurnrate);


        _totalSupply = _totalSupply.mul(_remainrate);
        _totalSupply = _totalSupply.sub(_totalSupply.mod(10000));
        _totalSupply = _totalSupply.div(10000);

        
        if (_totalSupply > MAX_SUPPLY) {
            _totalSupply = MAX_SUPPLY;
        }

        _gonsPerFragment = TOTAL_GONS.div(_totalSupply);

        emit LogBurn(decayBurnrate, _totalSupply);
    }
    
    function _burn(address account, uint256 amount) public onlyOwner {
        require(account != address(0), "burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);
        
        uint256 gonValue = amount.mul(_gonsPerFragment);
        _gonBalances[account] = _gonBalances[account].sub(gonValue, "burn amount exceeds balance");
        
        _totalSupply = _totalSupply.sub(amount, "burn amount exceeds balance");
        emit Transfer(account, address(0), amount);
    }
    
    
    function name() public pure returns (string memory) {
        return _name;
    }
    
    function symbol() public pure returns (string memory) {
        return _symbol;
    }
    
    function decimals() public view override returns (uint8) {
        return _decimals;
    }
    
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view override returns (uint256)
    {
        return _gonBalances[account].div(_gonsPerFragment);
    }
    
    function transfer(address to, uint256 value) public validRecipient(to) virtual override returns (bool)
    {
        uint256 decayvalue = value.mul(transBurnrate); //example::2.5%->250/10000
        decayvalue = decayvalue.sub(decayvalue.mod(10000));
        decayvalue = decayvalue.div(10000);
        
        uint256 leftValue = value.sub(decayvalue);
        
        uint256 gonValue = value.mul(_gonsPerFragment);
        uint256 leftgonValue = value.sub(decayvalue);
        leftgonValue = leftgonValue.mul(_gonsPerFragment);
        _gonBalances[msg.sender] = _gonBalances[msg.sender].sub(gonValue);
        _gonBalances[to] = _gonBalances[to].add(leftgonValue);
        
        _totalSupply = _totalSupply.sub(decayvalue);
        
        emit Transfer(msg.sender, address(0x0), decayvalue);
        emit Transfer(msg.sender, to, leftValue);
        return true;
    }
    
    function allowance(address owner_, address spender) public view virtual override returns (uint256)
    {
        return _allowedFragments[owner_][spender];
    }

    function approve(address spender, uint256 value) public virtual override returns (bool)
    {
        _allowedFragments[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }
    
    function transferFrom(address from, address to, uint256 value) public validRecipient(to) virtual override returns (bool)
    {
        _allowedFragments[from][msg.sender] = _allowedFragments[from][msg.sender].sub(value);
        
        uint256 decayvalue = value.mul(transBurnrate); //example::2.5%->250/10000
        decayvalue = decayvalue.sub(decayvalue.mod(10000));
        decayvalue = decayvalue.div(10000);
        
        uint256 leftValue = value.sub(decayvalue);
        
        uint256 gonValue = value.mul(_gonsPerFragment);
        uint256 leftgonValue = value.sub(decayvalue);
        leftgonValue = leftgonValue.mul(_gonsPerFragment);
        
        _totalSupply = _totalSupply.sub(decayvalue);
        
        _gonBalances[from] = _gonBalances[from].sub(gonValue);
        _gonBalances[to] = _gonBalances[to].add(leftgonValue);
        
        emit Transfer(from, address(0x0), decayvalue);
        emit Transfer(from, to, leftValue);

        return true;
    }
    
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool)
    {
        _allowedFragments[msg.sender][spender] =
        _allowedFragments[msg.sender][spender].add(addedValue);
        emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool)
    {
        uint256 oldValue = _allowedFragments[msg.sender][spender];
        if (subtractedValue >= oldValue) {
            _allowedFragments[msg.sender][spender] = 0;
        } else {
            _allowedFragments[msg.sender][spender] = oldValue.sub(subtractedValue);
        }
        emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
        return true;
    }
    
    function changetransBurnrate(uint256 _newtransBurnrate) public onlyOwner returns (bool) {
        require(_newtransBurnrate <= maxtransBurnrate, "too high value");
        transBurnrate = _newtransBurnrate;
        return true;
    }
    
    function changedecayBurnrate(uint256 _newdecayBurnrate) public onlyOwner returns (bool) {
        require(_newdecayBurnrate <= maxdecayBurnrate, "too high value");
        decayBurnrate = _newdecayBurnrate;
        return true;
    }

    function mint(address account, uint256 amount) public onlyOwner {
        require(account != address(0));
        require(maxSupplyHit != true, "max supply hit");
        
        uint256 supply = totalSupply();
        if (supply.add(amount) >= max_supply) {
            amount = max_supply.sub(supply);
            maxSupplyHit = true;
        }
        
        _beforeTokenTransfer(address(0), account, amount);
        uint256 gonValue = amount.mul(_gonsPerFragment);

        _totalSupply = _totalSupply.add(amount);
        _gonBalances[account] = _gonBalances[account].add(gonValue);
        emit Transfer(address(0), account, amount);
        
        _moveDelegates(address(0), _delegates[account], amount);
    }
   
    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
    
    function approveAndCall(address _spender, uint256 _tokens, bytes calldata _data) external returns (bool) {
        approve(_spender, _tokens);
        Callable(_spender).receiveApproval(msg.sender, _tokens, address(this), _data);
        return true;
    }

    function transferAndCall(address _to, uint256 _tokens, bytes calldata _data) external returns (bool) {
        uint256 _balanceBefore = balanceOf(_to);
        transfer(_to, _tokens);
        uint256 _tokensReceived = balanceOf(_to) - _balanceBefore;
        uint32 _size;
        assembly {
            _size := extcodesize(_to)
        }
        if (_size > 0) {
            require(Callable(_to).tokenCallback(msg.sender, _tokensReceived, _data));
        }
        return true;
    }
    
    // Both the MasterChef contracts will lock the GRAVY-ETH LP tokens they receive from their staking/unstaking fees here (ensuring liquidity forever).
    // This function allows GRAVY token holders to decide what to do with the locked LP tokens in the future
    function migrateLockedLPTokens(address _to, uint256 _amount) public onlyOwner {
        IERC20 gravyPool = IERC20(gravyPoolAddress);
        require(_amount > 0 && _amount <= gravyPool.balanceOf(address(this)), "bad amount");
        gravyPool.transfer(_to, _amount);
    }
    
    // Sets the addresses of the MasterChef farming contract, and the Uniswap GRAVY-ETH LP token
    function setContractAddresses(address _masterchefAddress, address _gravyPoolAddress) public onlyOwner {
        if (_masterchefAddress != address(0)) masterchefAddress = _masterchefAddress;
        if (_gravyPoolAddress != address(0)) gravyPoolAddress = _gravyPoolAddress;
    }

    /**
     * @notice Delegate votes from `msg.sender` to `delegatee`
     * @param delegator The address to get delegatee for
     */
    function delegates(address delegator)
        external
        view
        returns (address)
    {
        return _delegates[delegator];
    }

   /**
    * @notice Delegate votes from `msg.sender` to `delegatee`
    * @param delegatee The address to delegate votes to
    */
    function delegate(address delegatee) external {
        return _delegate(msg.sender, delegatee);
    }