creation_sourcecode: # @version 0.3.10
  """
  @title Boost Delegation V3
  @author CurveFi
  """
  
  event Approval:
      _owner: indexed(address)
      _spender: indexed(address)
      _value: uint256
  
  event Transfer:
      _from: indexed(address)
      _to: indexed(address)
      _value: uint256
  
  event Boost:
      _from: indexed(address)
      _to: indexed(address)
      _bias: uint256
      _slope: uint256
      _start: uint256
  
  interface VotingEscrow:
      def balanceOf(_user: address) -> uint256: view
      def totalSupply() -> uint256: view
      def locked__end(_user: address) -> uint256: view
  
  
  struct Point:
      bias: uint256
      slope: uint256
      ts: uint256
  
  
  NAME: constant(String[32]) = "Vote-Escrowed Boost"
  SYMBOL: constant(String[8]) = "veBoost"
  VERSION: constant(String[8]) = "v3.0.0"
  
  EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")
  PERMIT_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
  
  WEEK: constant(uint256) = 86400 * 7
  
  
  DOMAIN_SEPARATOR: immutable(bytes32)
  VE: immutable(address)
  
  
  allowance: public(HashMap[address, HashMap[address, uint256]])
  nonces: public(HashMap[address, uint256])
  
  delegated: public(HashMap[address, Point])
  delegated_slope_changes: public(HashMap[address, HashMap[uint256, uint256]])
  
  received: public(HashMap[address, Point])
  received_slope_changes: public(HashMap[address, HashMap[uint256, uint256]])
  
  @external
  def __init__(_ve: address):
      DOMAIN_SEPARATOR = keccak256(_abi_encode(EIP712_TYPEHASH, keccak256(NAME), keccak256(VERSION), chain.id, self, block.prevhash))
      VE = _ve
  
      log Transfer(ZERO_ADDRESS, msg.sender, 0)
  
  
  @view
  @internal
  def _checkpoint_read(_user: address, _delegated: bool) -> Point:
      point: Point = empty(Point)
  
      if _delegated:
          point = self.delegated[_user]
      else:
          point = self.received[_user]
  
      if point.ts == 0:
          point.ts = block.timestamp
  
      if point.ts == block.timestamp:
          return point
  
      ts: uint256 = (point.ts / WEEK) * WEEK
      for _ in range(255):
          ts += WEEK
  
          dslope: uint256 = 0
          if block.timestamp < ts:
              ts = block.timestamp
          else:
              if _delegated:
                  dslope = self.delegated_slope_changes[_user][ts]
              else:
                  dslope = self.received_slope_changes[_user][ts]
  
          point.bias -= point.slope * (ts - point.ts)
          point.slope -= dslope
          point.ts = ts
  
          if ts == block.timestamp:
              break
  
      return point
  
  
  @internal
  def _checkpoint_write(_user: address, _delegated: bool) -> Point:
      point: Point = empty(Point)
  
      if _delegated:
          point = self.delegated[_user]
      else:
          point = self.received[_user]
  
      if point.ts == 0:
          point.ts = block.timestamp
  
      if point.ts == block.timestamp:
          return point
  
      dbias: uint256 = 0
      ts: uint256 = (point.ts / WEEK) * WEEK
      for _ in range(255):
          ts += WEEK
  
          dslope: uint256 = 0
          if block.timestamp < ts:
              ts = block.timestamp
          else:
              if _delegated:
                  dslope = self.delegated_slope_changes[_user][ts]
              else:
                  dslope = self.received_slope_changes[_user][ts]
  
          amount: uint256 = point.slope * (ts - point.ts)
  
          dbias += amount
          point.bias -= amount
          point.slope -= dslope
          point.ts = ts
  
          if ts == block.timestamp:
              break
  
      if _delegated == False and dbias != 0:  # received boost
          log Transfer(_user, ZERO_ADDRESS, dbias)
  
      return point
  
  
  @view
  @internal
  def _balance_of(_user: address) -> uint256:
      amount: uint256 = VotingEscrow(VE).balanceOf(_user)
  
      point: Point = self._checkpoint_read(_user, True)
      amount -= (point.bias - point.slope * (block.timestamp - point.ts))
  
      point = self._checkpoint_read(_user, False)
      amount += (point.bias - point.slope * (block.timestamp - point.ts))
      return amount
  
  
  @internal
  def _boost(_from: address, _to: address, _amount: uint256, _endtime: uint256):
      assert _to not in [_from, ZERO_ADDRESS]
      assert _amount != 0
      assert _endtime > block.timestamp
      assert _endtime % WEEK == 0
      assert _endtime <= VotingEscrow(VE).locked__end(_from)
  
      # checkpoint delegated point
      point: Point = self._checkpoint_write(_from, True)
      assert _amount <= VotingEscrow(VE).balanceOf(_from) - (point.bias - point.slope * (block.timestamp - point.ts))
  
      # calculate slope and bias being added
      slope: uint256 = _amount / (_endtime - block.timestamp)
      bias: uint256 = slope * (_endtime - block.timestamp)
  
      # update delegated point
      point.bias += bias
      point.slope += slope
  
      # store updated values
      self.delegated[_from] = point
      self.delegated_slope_changes[_from][_endtime] += slope
  
      # update received amount
      point = self._checkpoint_write(_to, False)
      point.bias += bias
      point.slope += slope
  
      # store updated values
      self.received[_to] = point
      self.received_slope_changes[_to][_endtime] += slope
  
      log Transfer(_from, _to, _amount)
      log Boost(_from, _to, bias, slope, block.timestamp)
  
      # also checkpoint received and delegated
      self.received[_from] = self._checkpoint_write(_from, False)
      self.delegated[_to] = self._checkpoint_write(_to, True)
  
  
  @external
  def boost(_to: address, _amount: uint256, _endtime: uint256, _from: address = msg.sender):
      # reduce approval if necessary
      if _from != msg.sender:
          allowance: uint256 = self.allowance[_from][msg.sender]
          if allowance != MAX_UINT256:
              self.allowance[_from][msg.sender] = allowance - _amount
              log Approval(_from, msg.sender, allowance - _amount)
  
      self._boost(_from, _to, _amount, _endtime)
  
  
  @external
  def checkpoint_user(_user: address):
      self.delegated[_user] = self._checkpoint_write(_user, True)
      self.received[_user] = self._checkpoint_write(_user, False)
  
  
  @external
  def approve(_spender: address, _value: uint256) -> bool:
      self.allowance[msg.sender][_spender] = _value
  
      log Approval(msg.sender, _spender, _value)
      return True
  
  
  @external
  def permit(_owner: address, _spender: address, _value: uint256, _deadline: uint256, _v: uint8, _r: bytes32, _s: bytes32) -> bool:
      assert _owner != ZERO_ADDRESS
      assert block.timestamp <= _deadline
  
      nonce: uint256 = self.nonces[_owner]
      digest: bytes32 = keccak256(
          concat(
              b"\x19\x01",
              DOMAIN_SEPARATOR,
              keccak256(_abi_encode(PERMIT_TYPEHASH, _owner, _spender, _value, nonce, _deadline))
          )
      )
  
      assert ecrecover(digest, convert(_v, uint256), convert(_r, uint256), convert(_s, uint256)) == _owner
  
      self.allowance[_owner][_spender] = _value
      self.nonces[_owner] = nonce + 1
  
      log Approval(_owner, _spender, _value)
      return True
  
  
  @external
  def increaseAllowance(_spender: address, _added_value: uint256) -> bool:
      allowance: uint256 = self.allowance[msg.sender][_spender] + _added_value
      self.allowance[msg.sender][_spender] = allowance
  
      log Approval(msg.sender, _spender, allowance)
      return True
  
  
  @external
  def decreaseAllowance(_spender: address, _subtracted_value: uint256) -> bool:
      allowance: uint256 = self.allowance[msg.sender][_spender] - _subtracted_value
      self.allowance[msg.sender][_spender] = allowance
  
      log Approval(msg.sender, _spender, allowance)
      return True
  
  
  @view
  @external
  def balanceOf(_user: address) -> uint256:
      return self._balance_of(_user)
  
  
  @view
  @external
  def adjusted_balance_of(_user: address) -> uint256:
      return self._balance_of(_user)
  
  
  @external
  def adjusted_balance_of_write(_user: address) -> uint256:
      amount: uint256 = VotingEscrow(VE).balanceOf(_user)
  
      point: Point = self._checkpoint_write(_user, True)
      self.delegated[_user] = point
      amount -= (point.bias - point.slope * (block.timestamp - point.ts))
  
      point = self._checkpoint_write(_user, False)
      self.received[_user] = point
      amount += (point.bias - point.slope * (block.timestamp - point.ts))
  
      return amount
  
  @view
  @external
  def totalSupply() -> uint256:
      return VotingEscrow(VE).totalSupply()
  
  
  @view
  @external
  def delegated_balance(_user: address) -> uint256:
      point: Point = self._checkpoint_read(_user, True)
      return point.bias - point.slope * (block.timestamp - point.ts)
  
  
  @view
  @external
  def received_balance(_user: address) -> uint256:
      point: Point = self._checkpoint_read(_user, False)
      return point.bias - point.slope * (block.timestamp - point.ts)
  
  
  @view
  @external
  def delegable_balance(_user: address) -> uint256:
      point: Point = self._checkpoint_read(_user, True)
      return VotingEscrow(VE).balanceOf(_user) - (point.bias - point.slope * (block.timestamp - point.ts))
  
  
  @pure
  @external
  def name() -> String[32]:
      return NAME
  
  
  @pure
  @external
  def symbol() -> String[8]:
      return SYMBOL
  
  
  @pure
  @external
  def decimals() -> uint8:
      return 18
  
  
  @pure
  @external
  def DOMAIN_SEPARATOR() -> bytes32:
      return DOMAIN_SEPARATOR
  
  
  @pure
  @external
  def VE() -> address:
      return VE