Contract Name:
Vyper_contract
Contract Source Code:
File 1 of 1 : Vyper_contract
# @version 0.2.16
"""
@title Simple Vesting Escrow
@author Curve Finance, Yearn Finance
@license MIT
@notice Vests ERC20 tokens for a single address
@dev Intended to be deployed many times via `VotingEscrowFactory`
"""
from vyper.interfaces import ERC20
event Fund:
recipient: indexed(address)
amount: uint256
event Claim:
recipient: indexed(address)
claimed: uint256
event RugPull:
recipient: address
rugged: uint256
event CommitOwnership:
admin: address
event ApplyOwnership:
admin: address
recipient: public(address)
token: public(ERC20)
start_time: public(uint256)
end_time: public(uint256)
cliff_length: public(uint256)
total_locked: public(uint256)
total_claimed: public(uint256)
disabled_at: public(uint256)
initialized: public(bool)
admin: public(address)
future_admin: public(address)
@external
def __init__():
# ensure that the original contract cannot be initialized
self.initialized = True
@external
@nonreentrant('lock')
def initialize(
admin: address,
token: address,
recipient: address,
amount: uint256,
start_time: uint256,
end_time: uint256,
cliff_length: uint256,
) -> bool:
"""
@notice Initialize the contract.
@dev This function is seperate from `__init__` because of the factory pattern
used in `VestingEscrowFactory.deploy_vesting_contract`. It may be called
once per deployment.
@param admin Admin address
@param token Address of the ERC20 token being distributed
@param recipient Address to vest tokens for
@param amount Amount of tokens being vested for `recipient`
@param start_time Epoch time at which token distribution starts
@param end_time Time until everything should be vested
@param cliff_length Duration after which the first portion vests
"""
assert not self.initialized # dev: can only initialize once
self.initialized = True
self.token = ERC20(token)
self.admin = admin
self.start_time = start_time
self.end_time = end_time
self.cliff_length = cliff_length
assert self.token.transferFrom(msg.sender, self, amount) # dev: could not fund escrow
self.recipient = recipient
self.disabled_at = end_time # Set to maximum time
self.total_locked = amount
log Fund(recipient, amount)
return True
@internal
@view
def _total_vested_at(time: uint256 = block.timestamp) -> uint256:
start: uint256 = self.start_time
end: uint256 = self.end_time
locked: uint256 = self.total_locked
if time < start + self.cliff_length:
return 0
return min(locked * (time - start) / (end - start), locked)
@internal
@view
def _unclaimed(time: uint256 = block.timestamp) -> uint256:
return self._total_vested_at(time) - self.total_claimed
@external
@view
def unclaimed() -> uint256:
"""
@notice Get the number of unclaimed, vested tokens for recipient
"""
# NOTE: if `rug_pull` is activated, limit by the activation timestamp
return self._unclaimed(min(block.timestamp, self.disabled_at))
@internal
@view
def _locked(time: uint256 = block.timestamp) -> uint256:
return self.total_locked - self._total_vested_at(time)
@external
@view
def locked() -> uint256:
"""
@notice Get the number of locked tokens for recipient
"""
# NOTE: if `rug_pull` is activated, limit by the activation timestamp
return self._locked(min(block.timestamp, self.disabled_at))
@external
def claim(beneficiary: address = msg.sender, amount: uint256 = MAX_UINT256):
"""
@notice Claim tokens which have vested
@param beneficiary Address to transfer claimed tokens to
@param amount Amount of tokens to claim
"""
assert msg.sender == self.recipient # dev: not recipient
claim_period_end: uint256 = min(block.timestamp, self.disabled_at)
claimable: uint256 = min(self._unclaimed(claim_period_end), amount)
self.total_claimed += claimable
assert self.token.transfer(beneficiary, claimable)
log Claim(beneficiary, claimable)
@external
def rug_pull():
"""
@notice Disable further flow of tokens and clawback the unvested part to admin
"""
assert msg.sender == self.admin # dev: admin only
# NOTE: Rugging more than once is futile
self.disabled_at = block.timestamp
ruggable: uint256 = self._locked()
assert self.token.transfer(self.admin, ruggable)
log RugPull(self.recipient, ruggable)
@external
def commit_transfer_ownership(addr: address):
"""
@notice Transfer ownership of the contract to `addr`
@param addr Address to have ownership transferred to
"""
assert msg.sender == self.admin # dev: admin only
self.future_admin = addr
log CommitOwnership(addr)
@external
def apply_transfer_ownership():
"""
@notice Apply pending ownership transfer
"""
assert msg.sender == self.future_admin # dev: future admin only
self.admin = msg.sender
self.future_admin = ZERO_ADDRESS
log ApplyOwnership(msg.sender)
@external
def renounce_ownership():
"""
@notice Renounce admin control of the escrow
"""
assert msg.sender == self.admin # dev: admin only
self.future_admin = ZERO_ADDRESS
self.admin = ZERO_ADDRESS
log ApplyOwnership(ZERO_ADDRESS)