contract UnifarmToken is Ownable {
  /// @notice EIP-20 token name for this token
  string public constant name = "UNIFARM Token";

  /// @notice EIP-20 token symbol for this token
  string public constant symbol = "UFARM";

  /// @notice EIP-20 token decimals for this token
  uint8 public constant decimals = 18;

  /// @notice Total number of tokens in circulation
  uint256 public totalSupply = 1000000000e18; // 1 billion UFARM

  using SafeMath for uint256;

  mapping(address => mapping(address => uint256)) internal allowances;

  mapping(address => uint256) internal balances;

  /// @notice A record of each accounts delegate
  mapping(address => address) public 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;

  mapping(address => uint256) public lockedTokens;

  /// @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 The EIP-712 typehash for the permit struct used by the contract
  bytes32 public constant PERMIT_TYPEHASH =
    keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

  /// @notice A record of states for signing / validating signatures
  mapping(address => uint256) 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, uint256 previousBalance, uint256 newBalance);

  /// @notice The standard EIP-20 transfer event
  event Transfer(address indexed from, address indexed to, uint256 amount);

  /// @notice The standard EIP-20 approval event
  event Approval(address indexed owner, address indexed spender, uint256 amount);

  /**
   * @notice Construct a new UFARM token
   * @param account The initial account to grant all the tokens
   */
  constructor(address account) Ownable(account) {
    balances[account] = uint256(totalSupply);
    emit Transfer(address(0), account, totalSupply);
  }

  /**
   * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
   * @param account The address of the account holding the funds
   * @param spender The address of the account spending the funds
   * @return The number of tokens approved
   */
  function allowance(address account, address spender) external view returns (uint256) {
    return allowances[account][spender];
  }

  /**
   * @notice Approve `spender` to transfer up to `amount` from `src`
   * @dev This will overwrite the approval amount for `spender`
   *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
   * @param spender The address of the account which may transfer tokens
   * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
   * @return Whether or not the approval succeeded
   */
  function approve(address spender, uint256 rawAmount) external returns (bool) {
    require(spender != address(0), "UFARM::approve: invalid spender address");

    uint256 amount;
    if (rawAmount == uint256(-1)) {
      amount = uint256(-1);
    } else {
      amount = rawAmount; //safe96(rawAmount, "UFARM::approve: amount exceeds 96 bits");
    }

    allowances[msg.sender][spender] = amount;

    emit Approval(msg.sender, spender, amount);
    return true;
  }

  /**
   * @dev Atomically increases the allowance granted to `spender` by the caller.
   *
   * This is an alternative to {approve} that can be used as a mitigation for
   * problems described in {IERC20-approve}.
   *
   * Emits an {Approval} event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `spender` cannot be the zero address.
   */
  function increaseAllowance(address spender, uint256 addedValue) external returns (bool) {
    require(spender != address(0), "UFARM::approve: invalid spender address");
    uint256 newAllowance = allowances[_msgSender()][spender].add(addedValue);
    allowances[_msgSender()][spender] = newAllowance;
    emit Approval(msg.sender, spender, newAllowance);
    return true;
  }

  /**
   * @dev Atomically decreases the allowance granted to `spender` by the caller.
   *
   * This is an alternative to {approve} that can be used as a mitigation for
   * problems described in {IERC20-approve}.
   *
   * Emits an {Approval} event indicating the updated allowance.
   *
   * Requirements:
   *
   * - `spender` cannot be the zero address.
   * - `spender` must have allowance for the caller of at least
   * `subtractedValue`.
   */
  function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool) {
    require(spender != address(0), "UFARM::approve: invalid spender address");
    uint256 currentAllowance = allowances[_msgSender()][spender];
    require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");

    allowances[_msgSender()][spender] = currentAllowance.sub(subtractedValue);
    emit Approval(msg.sender, spender, currentAllowance.sub(subtractedValue));
    return true;
  }

  /**
   * @notice Triggers an approval from owner to spends
   * @param owner The address to approve from
   * @param spender The address to be approved
   * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
   * @param deadline The time at which to expire the signature
   * @param v The recovery byte of the signature
   * @param r Half of the ECDSA signature pair
   * @param s Half of the ECDSA signature pair
   */
  function permit(
    address owner,
    address spender,
    uint256 rawAmount,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    uint256 amount;
    if (rawAmount == uint256(-1)) {
      amount = uint256(-1);
    } else {
      amount = rawAmount; //safe96(rawAmount, "UFARM::permit: amount exceeds 96 bits");
    }

    bytes32 domainSeparator =
      keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));
    bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, rawAmount, nonces[owner]++, deadline));
    bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    address signatory = ecrecover(digest, v, r, s);
    require(signatory != address(0), "UFARM::permit: invalid signature");
    require(signatory == owner, "UFARM::permit: unauthorized");
    require(block.timestamp <= deadline, "UFARM::permit: signature expired");

    allowances[owner][spender] = amount;

    emit Approval(owner, spender, amount);
  }

  /**
   * @notice Get the number of tokens held by the `account`
   * @param account The address of the account to get the balance of
   * @return The number of tokens held
   */
  function balanceOf(address account) external view returns (uint256) {
    return balances[account];
  }

  /**
   * @notice Transfer `amount` tokens from `msg.sender` to `dst`
   * @param dst The address of the destination account
   * @param rawAmount The number of tokens to transfer
   * @return Whether or not the transfer succeeded
   */
  function transfer(address dst, uint256 rawAmount) external returns (bool) {
    _transferTokens(msg.sender, dst, rawAmount);
    return true;
  }

  /**
   * @notice Transfer `amount` tokens from `src` to `dst`
   * @param src The address of the source account
   * @param dst The address of the destination account
   * @param rawAmount The number of tokens to transfer
   * @return Whether or not the transfer succeeded
   */
  function transferFrom(
    address src,
    address dst,
    uint256 rawAmount
  ) external returns (bool) {
    address spender = msg.sender;
    uint256 spenderAllowance = allowances[src][spender];

    if (spender != src && spenderAllowance != uint256(-1)) {
      uint256 newAllowance = spenderAllowance.sub(rawAmount, "UFARM::transferFrom: exceeds allowance");
      allowances[src][spender] = newAllowance;

      emit Approval(src, spender, newAllowance);
    }

    _transferTokens(src, dst, rawAmount);
    return true;
  }

  /**
   * @notice Delegate votes from `msg.sender` to `delegatee`
   * @param delegatee The address to delegate votes to
   */
  function delegate(address delegatee) external returns (bool) {
    _delegate(msg.sender, delegatee);
    return true;
  }

  /**
   * @notice Delegates votes from signatory to `delegatee`
   * @param delegatee The address to delegate votes to
   * @param nonce The contract state required to match the signature
   * @param expiry The time at which to expire the signature
   * @param v The recovery byte of the signature
   * @param r Half of the ECDSA signature pair
   * @param s Half of the ECDSA signature pair
   */
  function delegateBySig(
    address delegatee,
    uint256 nonce,
    uint256 expiry,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    require(block.timestamp <= expiry, "UFARM::delegateBySig: signature expired");
    bytes32 domainSeparator =
      keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this)));
    bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
    bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    address signatory = ecrecover(digest, v, r, s);
    require(signatory != address(0), "UFARM::delegateBySig: invalid signature");
    require(nonce == nonces[signatory]++, "UFARM::delegateBySig: invalid nonce");
    return _delegate(signatory, delegatee);
  }

  /**
   * @notice Gets the current votes balance for `account`
   * @param account The address to get votes balance
   * @return The number of current votes for `account`
   */
  function getCurrentVotes(address account) external view returns (uint256) {
    uint32 nCheckpoints = numCheckpoints[account];
    return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
  }

  /**
   * @notice Determine the prior number of votes for an account as of a block number
   * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
   * @param account The address of the account to check
   * @param blockNumber The block number to get the vote balance at
   * @return The number of votes the account had as of the given block
   */
  function getPriorVotes(address account, uint256 blockNumber) external view returns (uint256) {
    require(blockNumber < block.number, "UFARM::getPriorVotes: not yet determined");

    uint32 nCheckpoints = numCheckpoints[account];
    if (nCheckpoints == 0) {
      return 0;
    }

    // First check most recent balance
    if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
      return checkpoints[account][nCheckpoints - 1].votes;
    }

    // Next check implicit zero balance
    if (checkpoints[account][0].fromBlock > blockNumber) {
      return 0;
    }

    uint32 lower = 0;
    uint32 upper = nCheckpoints - 1;
    while (upper > lower) {
      uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
      Checkpoint memory cp = checkpoints[account][center];
      if (cp.fromBlock == blockNumber) {
        return cp.votes;
      } else if (cp.fromBlock < blockNumber) {
        lower = center;
      } else {
        upper = center - 1;
      }
    }
    return checkpoints[account][lower].votes;
  }

  /**
   * @notice burn the token of any token holder.
   * @dev balance should be greater than amount. function will revert will balance is less than amount.
   * @param holder the addrress of token holder.
   * @param amount number of tokens to burn.
   * @return true when burnToken succeeded.
   */

  function burnToken(address holder, uint256 amount) external onlyOwner returns (bool) {
    require(balances[holder] >= amount, "UFARM::burnToken: Insufficient balance");

    balances[holder] = balances[holder].sub(amount);
    totalSupply = totalSupply.sub(amount);
    _moveDelegates(delegates[holder], delegates[address(0)], amount);
    return true;
  }

  /**
   * @notice lock the token of any token holder.
   * @dev balance should be greater than amount. function will revert will balance is less than amount.
   * @param holder the addrress of token holder.
   * @param amount number of tokens to burn.
   * @return true when lockToken succeeded.
   */

  function lockToken(address holder, uint256 amount) external onlyOwner returns (bool) {
    require(balances[holder] >= amount, "UFARM::burnToken: Insufficient balance");

    balances[holder] = balances[holder].sub(amount);
    lockedTokens[holder] = lockedTokens[holder].add(amount);
    _moveDelegates(delegates[holder], delegates[address(0)], amount);
    return true;
  }

  /**
   * @notice unLock the token of any token holder.
   * @dev locked balance should be greater than amount. function will revert will locked balance is less than amount.
   * @param holder the addrress of token holder.
   * @param amount number of tokens to burn.
   * @return true when unLockToken succeeded.
   */

  function unlockToken(address holder, uint256 amount) external onlyOwner returns (bool) {
    require(lockedTokens[holder] >= amount, "UFARM::unlockToken: OverflowLocked balance");

    lockedTokens[holder] = lockedTokens[holder].sub(amount);
    balances[holder] = balances[holder].add(amount);
    _moveDelegates(delegates[address(0)], delegates[holder], amount);
    return true;
  }

  /**
   * @notice set the delegatee.
   * @dev delegatee address should not be zero address.
   * @param delegator the addrress of token holder.
   * @param delegatee number of tokens to burn.
   */

  function _delegate(address delegator, address delegatee) internal {
    require(delegatee != address(0), "UFARM::_delegate: invalid delegatee address");
    address currentDelegate = delegates[delegator];
    uint256 delegatorBalance = balances[delegator];
    delegates[delegator] = delegatee;

    emit DelegateChanged(delegator, currentDelegate, delegatee);

    _moveDelegates(currentDelegate, delegatee, delegatorBalance);
  }

  /**
   * @notice transfer tokens to src --> dst.
   * @dev src address should be valid ethereum address.
   * @dev dst address should be valid ethereum address.
   * @dev amount should be greater than zero.
   * @param src the source address.
   * @param dst the destination address.
   * @param amount number of token to transfer.
   */

  function _transferTokens(
    address src,
    address dst,
    uint256 amount
  ) internal {
    require(src != address(0), "UFARM::_transferTokens: cannot transfer from the zero address");
    require(dst != address(0), "UFARM::_transferTokens: cannot transfer to the zero address");
    require(amount > 0, "UFARM::_transferTokens: invalid amount wut??");

    balances[src] = balances[src].sub(amount, "UFARM::_transferTokens: exceeds balance");
    balances[dst] = balances[dst].add(amount);
    emit Transfer(src, dst, amount);

    _moveDelegates(delegates[src], delegates[dst], amount);
  }

  /**
   * @notice transfer the vote token.
   * @dev srcRep address should be valid ethereum address.
   * @dev dstRep address should be valid ethereum address.
   * @dev amount should be greater than zero.
   * @param srcRep the source vote address.
   * @param dstRep the destination vote address.
   * @param amount number of vote token to transfer.
   */

  function _moveDelegates(
    address srcRep,
    address dstRep,
    uint256 amount
  ) internal {
    if (srcRep != dstRep && amount > 0) {
      if (srcRep != address(0)) {
        uint32 srcRepNum = numCheckpoints[srcRep];
        uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
        uint256 srcRepNew = srcRepOld.sub(amount);
        _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
      }

      if (dstRep != address(0)) {
        uint32 dstRepNum = numCheckpoints[dstRep];
        uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
        uint256 dstRepNew = dstRepOld.add(amount);
        _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
      }
    }
  }

  /**
   * @notice write checkpoint for delegatee.
   * @dev blocknumber should be uint32.
   * @param delegatee the address of delegatee.
   * @param nCheckpoints no of checkpoints.
   * @param oldVotes number of old votes.
   * @param newVotes number of new votes.
   */

  function _writeCheckpoint(
    address delegatee,
    uint32 nCheckpoints,
    uint256 oldVotes,
    uint256 newVotes
  ) internal {
    uint32 blockNumber = safe32(block.number, "UFARM::_writeCheckpoint: block number exceeds 32 bits");

    if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
      checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
    } else {
      checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
      numCheckpoints[delegatee] = nCheckpoints + 1;
    }

    emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
  }

  /**
   * @notice safe32 function using for uint32 type.
   * @param n the data.
   * @param errorMessage set the errorMessage.
   * @return uint32 data.
   */

  function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {
    require(n < 2**32, errorMessage);
    return uint32(n);
  }

  /**
   * @notice get the chainId from inline assembly.
   * @return uint256 chainId of Node.
   */

  function getChainId() internal pure returns (uint256) {
    uint256 chainId;
    assembly {
      chainId := chainid()
    }
    return chainId;
  }
}
function _moveDelegates(
    address srcRep,
    address dstRep,
    uint256 amount
  ) internal {
    if (srcRep != dstRep && amount > 0) {
      if (srcRep != address(0)) {
        uint32 srcRepNum = numCheckpoints[srcRep];
        uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
        uint256 srcRepNew = srcRepOld.sub(amount);
        _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
      }

      if (dstRep != address(0)) {
        uint32 dstRepNum = numCheckpoints[dstRep];
        uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
        uint256 dstRepNew = dstRepOld.add(amount);
        _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
      }
    }
  }
/**
 * @notice transfer the vote token.
 * @dev srcRep address should be valid ethereum address.
 * @dev dstRep address should be valid ethereum address.
 * @dev amount should be greater than zero.
 * @param srcRep the source vote address.
 * @param dstRep the destination vote address.
 * @param amount number of vote token to transfer.
 */