ETH Price: $2,417.99 (-2.19%)
Gas: 3.08 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim204236162024-07-31 2:42:1197 days ago1722393731IN
0xbF98cc85...Bb6911ecF
0 ETH0.000277013.95561487
Claim203955452024-07-27 4:38:23100 days ago1722055103IN
0xbF98cc85...Bb6911ecF
0 ETH0.00008951.5337546
Claim203001972024-07-13 21:13:47114 days ago1720905227IN
0xbF98cc85...Bb6911ecF
0 ETH0.000148951.97417381
Claim202289182024-07-03 22:18:11124 days ago1720045091IN
0xbF98cc85...Bb6911ecF
0 ETH0.000551967.31529389
Claim201368202024-06-21 1:29:35137 days ago1718933375IN
0xbF98cc85...Bb6911ecF
0 ETH0.000214312.84037269
Claim201082822024-06-17 1:38:35141 days ago1718588315IN
0xbF98cc85...Bb6911ecF
0 ETH0.000291953.86928202
Claim200844142024-06-13 17:33:23144 days ago1718300003IN
0xbF98cc85...Bb6911ecF
0 ETH0.0009178812.16488954
Claim200365482024-06-07 1:04:35151 days ago1717722275IN
0xbF98cc85...Bb6911ecF
0 ETH0.0007644610.13151533
Claim199844412024-05-30 18:27:47158 days ago1717093667IN
0xbF98cc85...Bb6911ecF
0 ETH0.0011973620.51900361
Claim199566282024-05-26 21:06:23162 days ago1716757583IN
0xbF98cc85...Bb6911ecF
0 ETH0.000427087.31893165
Claim199246672024-05-22 9:56:11166 days ago1716371771IN
0xbF98cc85...Bb6911ecF
0 ETH0.000502486.65950227
Claim199141652024-05-20 22:39:35168 days ago1716244775IN
0xbF98cc85...Bb6911ecF
0 ETH0.001946425.79595534
Claim198916122024-05-17 18:56:11171 days ago1715972171IN
0xbF98cc85...Bb6911ecF
0 ETH0.000328714.35646477
Claim198215962024-05-07 23:54:35181 days ago1715126075IN
0xbF98cc85...Bb6911ecF
0 ETH0.000330144.37541802
Claim198088632024-05-06 5:08:35182 days ago1714972115IN
0xbF98cc85...Bb6911ecF
0 ETH0.000328354.351671
Claim197921992024-05-03 21:13:47185 days ago1714770827IN
0xbF98cc85...Bb6911ecF
0 ETH0.00050096.63853196
Claim197821472024-05-02 11:29:47186 days ago1714649387IN
0xbF98cc85...Bb6911ecF
0 ETH0.000495276.56386942
Claim197549152024-04-28 16:09:23190 days ago1714320563IN
0xbF98cc85...Bb6911ecF
0 ETH0.000715059.47672839
Claim197433052024-04-27 1:10:11192 days ago1714180211IN
0xbF98cc85...Bb6911ecF
0 ETH0.000437045.7922321
Claim197287262024-04-25 0:12:35194 days ago1714003955IN
0xbF98cc85...Bb6911ecF
0 ETH0.00065398.6663387
Claim197010212024-04-21 3:12:35198 days ago1713669155IN
0xbF98cc85...Bb6911ecF
0 ETH0.000492276.52412952
Claim196877272024-04-19 6:33:23199 days ago1713508403IN
0xbF98cc85...Bb6911ecF
0 ETH0.000635458.42178149
Claim196495902024-04-13 22:23:47205 days ago1713047027IN
0xbF98cc85...Bb6911ecF
0 ETH0.00830194110.02653831
Claim195889892024-04-05 10:37:35213 days ago1712313455IN
0xbF98cc85...Bb6911ecF
0 ETH0.001173515.55257571
Claim194931872024-03-22 22:38:47227 days ago1711147127IN
0xbF98cc85...Bb6911ecF
0 ETH0.0015087319.99537182
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
177913612023-07-28 11:48:47465 days ago1690544927  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.