ETH Price: $3,162.07 (+1.64%)

Contract

0x39f8B8334005Db22fd31745D43125572867e000d
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
168189092023-03-13 11:55:35616 days ago1678708535  Contract Creation0 ETH
Loading...
Loading

Minimal Proxy Contract for 0x484fd04c598a095360df89bf85ab34c37127aa39

Contract Name:
Vyper_contract

Compiler Version
vyper:0.3.7

Optimization Enabled:
N/A

Other Settings:
GNU GPLv3 license

Contract Source Code (Vyper language format)

# @version 0.3.7

"""
@title Vesting Escrow
@author Curve Finance, Yearn Finance, Lido Finance
@license GPL-3.0
@notice Vests ERC20 tokens for a single address
@dev Intended to be deployed many times via `VotingEscrowFactory`
"""

from vyper.interfaces import ERC20


interface IVestingEscrowFactory:
    def voting_adapter() -> address: nonpayable
    def owner() -> address: nonpayable
    def manager() -> address: nonpayable


event VestingEscrowInitialized:
    factory: indexed(address)
    recipient: indexed(address)
    token: indexed(address)
    amount: uint256
    start_time: uint256
    end_time: uint256
    cliff_length: uint256
    is_fully_revokable: bool


event Claim:
    beneficiary: indexed(address)
    claimed: uint256


event UnvestedTokensRevoked:
    recoverer: indexed(address)
    revoked: uint256


event VestingFullyRevoked:
    recoverer: indexed(address)
    revoked: uint256


event ERC20Recovered:
    token: address
    amount: uint256


event ETHRecovered:
    amount: uint256


recipient: public(address)
token: public(ERC20)
start_time: public(uint256)
end_time: public(uint256)
cliff_length: public(uint256)
factory: public(IVestingEscrowFactory)
total_locked: public(uint256)
is_fully_revokable: public(bool)

total_claimed: public(uint256)
disabled_at: public(uint256)
initialized: public(bool)
is_fully_revoked: public(bool)


@external
def __init__():
    """
    @notice Initialize source contract implementation.
    """
    # ensure that the original contract cannot be initialized
    self.initialized = True


@external
def initialize(
    token: address,
    amount: uint256,
    recipient: address,
    start_time: uint256,
    end_time: uint256,
    cliff_length: uint256,
    is_fully_revokable: bool,
    factory: address,
) -> bool:
    """
    @notice Initialize the contract.
    @dev This function is separate from `__init__` because of the factory pattern
         used in `VestingEscrowFactory.deploy_vesting_contract`. It may be called
         once per deployment.
    @param token Address of the ERC20 token being distributed
    @param amount Amount of the ERC20 token to be controleed by escrow
    @param recipient Address to vest tokens for
    @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
    @param factory Address of the parent factory
    """
    assert not self.initialized, "can only initialize once"
    self.initialized = True

    self.token = ERC20(token)
    self.is_fully_revokable = is_fully_revokable
    self.start_time = start_time
    self.end_time = end_time
    self.cliff_length = cliff_length

    assert ERC20(token).balanceOf(self) >= amount, "insufficient balance"

    self.total_locked = amount
    self.recipient = recipient
    self.disabled_at = end_time  # Set to maximum time
    self.factory = IVestingEscrowFactory(factory)
    log VestingEscrowInitialized(
        factory,
        recipient,
        token,
        amount,
        start_time,
        end_time,
        cliff_length,
        is_fully_revokable,
    )

    return True


@internal
@view
def _total_vested_at(time: uint256) -> 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() -> uint256:
    if self.is_fully_revoked:
        return 0
    claim_time: uint256 = min(block.timestamp, self.disabled_at)
    return self._total_vested_at(claim_time) - self.total_claimed


@external
@view
def unclaimed() -> uint256:
    """
    @notice Get the number of unclaimed, vested tokens for recipient
    """
    return self._unclaimed()


@internal
@view
def _locked() -> uint256:
    if block.timestamp >= self.disabled_at:
        return 0
    return self.total_locked - self._total_vested_at(block.timestamp)


@external
@view
def locked() -> uint256:
    """
    @notice Get the number of locked tokens for recipient
    """
    return self._locked()


@external
def claim(
    beneficiary: address = msg.sender, amount: uint256 = max_value(uint256)
) -> uint256:
    """
    @notice Claim tokens which have vested
    @param beneficiary Address to transfer claimed tokens to
    @param amount Amount of tokens to claim
    """
    self._check_sender_is_recipient()

    claimable: uint256 = min(self._unclaimed(), amount)
    self.total_claimed += claimable

    assert self.token.transfer(
        beneficiary, claimable, default_return_value=True
    ), "transfer failed"

    log Claim(beneficiary, claimable)

    return claimable


@external
def revoke_unvested():
    """
    @notice Disable further flow of tokens and revoke the unvested part to owner
    """
    self._check_sender_is_owner_or_manager()

    revokable: uint256 = self._locked()
    assert revokable > 0, "nothing to revoke"
    self.disabled_at = block.timestamp

    assert self.token.transfer(
        self._owner(), revokable, default_return_value=True
    ), "transfer failed"

    log UnvestedTokensRevoked(msg.sender, revokable)


@external
def revoke_all():
    """
    @notice Disable further flow of tokens and revoke all tokens to owner
    """
    self._check_sender_is_owner()
    assert self.is_fully_revokable, "not allowed for ordinary vesting"
    assert not self.is_fully_revoked, "already fully revoked"

    # NOTE: do not revoke extra tokens
    revokable: uint256 = self._locked() + self._unclaimed()
    assert revokable > 0, "nothing to revoke"

    self.is_fully_revoked = True
    self.disabled_at = block.timestamp

    assert self.token.transfer(
        self._owner(), revokable, default_return_value=True
    ), "transfer failed"

    log VestingFullyRevoked(msg.sender, revokable)


@external
def recover_erc20(token: address, amount: uint256):
    """
    @notice Recover ERC20 tokens to recipient
    @param token Address of the ERC20 token to be recovered
    @param amount Amount of the ERC20 token to be recovered
    """
    recoverable: uint256 = amount
    if token == self.token.address:
        available: uint256 = ERC20(token).balanceOf(self) - (
            self._locked() + self._unclaimed()
        )
        recoverable = min(recoverable, available)
    if recoverable > 0:
        assert ERC20(token).transfer(
            self.recipient, recoverable, default_return_value=True
        ), "transfer failed"
        log ERC20Recovered(token, recoverable)


@external
def recover_ether():
    """
    @notice Recover Ether to recipient
    """
    amount: uint256 = self.balance
    if amount != 0:
        self._safe_send_ether(self.recipient, amount)
        log ETHRecovered(amount)


@external
def aragon_vote(abi_encoded_params: Bytes[1000]):
    """
    @notice Participate Aragon vote using all available tokens on the contract's balance
    @param abi_encoded_params Abi encoded data for call. Can be obtained from VotingAdapter.encode_aragon_vote_calldata
    """
    self._check_sender_is_recipient()
    self._check_voting_adapter_is_set()
    raw_call(
        self.factory.voting_adapter(),
        _abi_encode(
            abi_encoded_params,
            method_id=method_id("aragon_vote(bytes)"),
        ),
        is_delegate_call=True,
    )


@external
def snapshot_set_delegate(abi_encoded_params: Bytes[1000]):
    """
    @notice Delegate Snapshot voting power of all available tokens on the contract's balance
    @param abi_encoded_params Abi encoded data for call. Can be obtained from VotingAdapter.encode_snapshot_set_delegate_calldata
    """
    self._check_sender_is_recipient()
    self._check_voting_adapter_is_set()
    raw_call(
        self.factory.voting_adapter(),
        _abi_encode(
            abi_encoded_params,
            method_id=method_id("snapshot_set_delegate(bytes)"),
        ),
        is_delegate_call=True,
    )


@external
def delegate(abi_encoded_params: Bytes[1000]):
    """
    @notice Delegate voting power of all available tokens on the contract's balance
    @param abi_encoded_params Abi encoded data for call. Can be obtained from VotingAdapter.encode_delegate_calldata
    """
    self._check_sender_is_recipient()
    self._check_voting_adapter_is_set()
    raw_call(
        self.factory.voting_adapter(),
        _abi_encode(
            abi_encoded_params,
            method_id=method_id("delegate(bytes)"),
        ),
        is_delegate_call=True,
    )


@internal
def _owner() -> address:
    return self.factory.owner()


@internal
def _manager() -> address:
    return self.factory.manager()


@internal
def _check_sender_is_owner_or_manager():
    assert (
        msg.sender == self._owner() or msg.sender == self._manager()
    ), "msg.sender not owner or manager"


@internal
def _check_sender_is_owner():
    assert msg.sender == self._owner(), "msg.sender not owner"


@internal
def _check_sender_is_recipient():
    assert msg.sender == self.recipient, "msg.sender not recipient"


@internal
def _check_voting_adapter_is_set():
    assert self.factory.voting_adapter() != empty(
        address
    ), "voting adapter not set"


@internal
def _safe_send_ether(_to: address, _value: uint256):
    """
    @notice Overcome 2300 gas limit on simple send
    """
    _response: Bytes[32] = raw_call(
        _to, empty(bytes32), value=_value, max_outsize=32
    )
    if len(_response) > 0:
        assert convert(_response, bool), "ETH transfer failed"

Contract ABI

[{"name":"VestingEscrowInitialized","inputs":[{"name":"factory","type":"address","indexed":true},{"name":"recipient","type":"address","indexed":true},{"name":"token","type":"address","indexed":true},{"name":"amount","type":"uint256","indexed":false},{"name":"start_time","type":"uint256","indexed":false},{"name":"end_time","type":"uint256","indexed":false},{"name":"cliff_length","type":"uint256","indexed":false},{"name":"is_fully_revokable","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"Claim","inputs":[{"name":"beneficiary","type":"address","indexed":true},{"name":"claimed","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"UnvestedTokensRevoked","inputs":[{"name":"recoverer","type":"address","indexed":true},{"name":"revoked","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"VestingFullyRevoked","inputs":[{"name":"recoverer","type":"address","indexed":true},{"name":"revoked","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ERC20Recovered","inputs":[{"name":"token","type":"address","indexed":false},{"name":"amount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ETHRecovered","inputs":[{"name":"amount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"initialize","inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"recipient","type":"address"},{"name":"start_time","type":"uint256"},{"name":"end_time","type":"uint256"},{"name":"cliff_length","type":"uint256"},{"name":"is_fully_revokable","type":"bool"},{"name":"factory","type":"address"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"unclaimed","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"locked","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"claim","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"claim","inputs":[{"name":"beneficiary","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"claim","inputs":[{"name":"beneficiary","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"revoke_unvested","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"revoke_all","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"recover_erc20","inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"recover_ether","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"aragon_vote","inputs":[{"name":"abi_encoded_params","type":"bytes"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"snapshot_set_delegate","inputs":[{"name":"abi_encoded_params","type":"bytes"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"delegate","inputs":[{"name":"abi_encoded_params","type":"bytes"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"recipient","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"token","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"start_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"end_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"cliff_length","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"factory","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"total_locked","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"is_fully_revokable","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"total_claimed","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"disabled_at","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"initialized","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"is_fully_revoked","inputs":[],"outputs":[{"name":"","type":"bool"}]}]

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.