ETH Price: $3,094.70 (+1.11%)
Gas: 5 Gwei

Contract

0xe852CFdE5be9156047F63278D496a2EC8b648EEf
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Claim202396502024-07-05 10:16:475 days ago1720174607IN
0xe852CFdE...C8b648EEf
0 ETH0.0006480311.10521934
Claim198676022024-05-14 10:17:4757 days ago1715681867IN
0xe852CFdE...C8b648EEf
0 ETH0.000445957.64218638
Claim196824802024-04-18 12:55:5983 days ago1713444959IN
0xe852CFdE...C8b648EEf
0 ETH0.0007575612.98217839
Claim194630282024-03-18 17:01:47114 days ago1710781307IN
0xe852CFdE...C8b648EEf
0 ETH0.0023363240.03715327
Claim189265292024-01-03 11:27:23189 days ago1704281243IN
0xe852CFdE...C8b648EEf
0 ETH0.0020163634.55406674
Claim188028062023-12-17 2:33:35206 days ago1702780415IN
0xe852CFdE...C8b648EEf
0 ETH0.0023348340.01161983
Claim184272032023-10-25 12:19:23259 days ago1698236363IN
0xe852CFdE...C8b648EEf
0 ETH0.0010640618.23465576
Claim180768012023-09-06 10:25:47308 days ago1693995947IN
0xe852CFdE...C8b648EEf
0 ETH0.0014325915.47842616

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
177918612023-07-28 13:30:35348 days ago1690551035  Contract Creation0 ETH
Loading...
Loading

Minimal Proxy Contract for 0x80429330c476857bafddf61772bfe1263d765a1f

Contract Name:
Vesting

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 4 : Vesting.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.13;

import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IVesting} from "src/interfaces/IVesting.sol";
import {IVestingFactory} from "src/interfaces/IVestingFactory.sol";

/*//////////////////////////////////////////////////////////////
                        CUSTOM ERROR
//////////////////////////////////////////////////////////////*/

error NotInitialised();
error Initialised();
error NoAccess();
error ZeroAddress();
error NoVestingData();
error StartLessThanNow();
error ZeroAmount();
error ZeroClaimAmount();
error AlreadyClaimed();
error AlreadyCancelled();
error Uncancellable();
error SameRecipient();

/*//////////////////////////////////////////////////////////////
                          CONTRACT
//////////////////////////////////////////////////////////////*/

/// @title Vesting Contract
contract Vesting is IVesting {
    // The recipient of the tokens
    address public recipient;

    uint40 public start;
    uint40 public duration;
    uint256 public amount;

    // Total amount of tokens which are claimed
    uint256 public totalClaimedAmount;

    // Flag for whether the vesting is cancellable or not
    bool public isCancellable;
    // Flag for whether the vesting is cancelled or not
    bool public cancelled;
    // Flag to check if its initialised
    bool private initialised;

    IVestingFactory public factory;

    /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    function initialise(address _recipient, uint40 _start, uint40 _duration, uint256 _amount, bool _isCancellable)
        external
        override
    {
        if (initialised) revert Initialised();
        initialised = true;

        recipient = _recipient;
        start = _start;
        duration = _duration;
        amount = _amount;
        isCancellable = _isCancellable;
        factory = IVestingFactory(msg.sender);
    }

    /*//////////////////////////////////////////////////////////////
                            MODIFIERS
    //////////////////////////////////////////////////////////////*/

    modifier onlyTreasury() {
        if (msg.sender != factory.treasury()) revert NoAccess();
        _;
    }

    modifier onlyOwner() {
        bool isOwner;
        if (isCancellable == false) {
            // If the vest is uncancellable, only the recipient can call the function
            isOwner = (msg.sender == recipient);
        } else {
            // If the vest is cancellable, only the treasury or the recipient can call the function
            isOwner = (msg.sender == recipient) || (msg.sender == factory.treasury());
        }
        if (!isOwner) revert NoAccess();
        _;
    }

    modifier onlyInit() {
        if (!initialised) revert NotInitialised();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice Calculates the amount of tokens which have been accrued to date.
    /// @notice This does not take into account the amount which has been claimed already.
    /// @return uint256 The amount of tokens which have been accrued to date.
    function getAccruedTokens() public view returns (uint256) {
        if (block.timestamp >= start + duration) {
            return amount;
        } else if (block.timestamp < start) {
            // Allows us to set up vests in advance
            return 0;
        } else {
            return (block.timestamp - start) * (amount / duration);
        }
    }

    /// @notice Calculates the amount of tokens which can be claimed.
    /// @return uint256 The amount of tokens which can be claimed.
    function getClaimableTokens() public view returns (uint256) {
        if (cancelled) {
            return 0;
        }

        uint256 accruedTokens = getAccruedTokens();

        // Calculate the amount of tokens which can be claimed
        uint256 tokensToClaim = accruedTokens - totalClaimedAmount;

        return tokensToClaim;
    }

    /// @notice Gets the vesting details of the contract.
    function getVestingDetails() public view returns (uint40, uint40, uint256, uint256, bool) {
        return (start, duration, amount, totalClaimedAmount, isCancellable);
    }

    function getTokens() public view returns (uint256) {
        // Assumes that the token has 18 decimals
        return amount / (10 ** 18);
    }

    /*//////////////////////////////////////////////////////////////
                          EXTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice Changes the recipient of vested tokens.
    /// @notice The old recipient will not be able to claim the tokens, the new recipient will be able to claim all unclaimed tokens accrued to date.
    /// @dev Can be called by the treasury or the recipient depending on whether the vest is cancellable or not.
    /// @param _newRecipient Address of the new recipient recieving the vested tokens.
    function changeRecipient(address _newRecipient) external onlyOwner onlyInit {
        if (_newRecipient == address(0)) revert ZeroAddress();
        if (_newRecipient == recipient) revert SameRecipient();
        if (start == 0) revert NoVestingData();
        if (cancelled) revert AlreadyCancelled();
        factory.changeRecipient(recipient, _newRecipient);
        recipient = _newRecipient;
    }

    /// @notice Cancels the vest and transfers the accrued amount to the recipient.
    /// @dev Can only be called by the treasury.
    function cancelVest() external onlyTreasury onlyInit {
        if (start < 1) revert NoVestingData();
        if (isCancellable == false) revert Uncancellable();
        if (cancelled) revert AlreadyCancelled();

        uint256 claimAmount = getClaimableTokens();

        if (claimAmount > 0) {
            totalClaimedAmount += claimAmount;
            factory.token().transfer(recipient, claimAmount);
        }

        cancelled = true;

        // Transfer the remainder of the tokens to the treasury
        factory.token().transfer(factory.treasury(), amount - totalClaimedAmount);
    }

    /// @notice A function allowing the recipient to claim the vested tokens.
    /// @notice The function returns unclaimed tokens to the treasury.
    /// @dev This function can be called by anyone.
    function claim() public override onlyInit {
        if (totalClaimedAmount >= amount) revert AlreadyClaimed();
        if (cancelled) revert AlreadyCancelled();

        uint256 claimAmount = getClaimableTokens();
        if (claimAmount < 1) revert ZeroClaimAmount();

        totalClaimedAmount += claimAmount;
        factory.token().transfer(recipient, claimAmount);
    }
}

File 2 of 4 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 3 of 4 : IVesting.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.0;

interface IVesting {
    function cancelled() external returns (bool);

    function totalClaimedAmount() external returns (uint256);
    
    function amount() external returns (uint256);

    function initialise(address _recipient, uint40 _start, uint40 _duration, uint256 _amount, bool _isCancellable)
        external;

    function claim() external;
}

File 4 of 4 : IVestingFactory.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.0;

import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

interface IVestingFactory {
    function treasury() external returns (address);

    function token() external returns (IERC20);

    function changeRecipient(address _oldRecipient, address _newRecipient) external;
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract ABI

[{"inputs":[],"name":"AlreadyCancelled","type":"error"},{"inputs":[],"name":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"Initialised","type":"error"},{"inputs":[],"name":"NoAccess","type":"error"},{"inputs":[],"name":"NoVestingData","type":"error"},{"inputs":[],"name":"NotInitialised","type":"error"},{"inputs":[],"name":"SameRecipient","type":"error"},{"inputs":[],"name":"Uncancellable","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroClaimAmount","type":"error"},{"inputs":[],"name":"amount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelVest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newRecipient","type":"address"}],"name":"changeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"duration","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IVestingFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAccruedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getClaimableTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVestingDetails","outputs":[{"internalType":"uint40","name":"","type":"uint40"},{"internalType":"uint40","name":"","type":"uint40"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint40","name":"_start","type":"uint40"},{"internalType":"uint40","name":"_duration","type":"uint40"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_isCancellable","type":"bool"}],"name":"initialise","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isCancellable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"start","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalClaimedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

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  ]
[ 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.