Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x2E731a002260b15Fc90eC9AdAAd8070a8fa09A9a
Balance 0 ETH
Nonce 1
Code Size 3093 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

3093 bytes
0x608060405234801561001057600080fd5b50600436106101415760003560e01c80634929e162116100b85780638341ee721161007c5780638341ee721461025a578063acc4bd0814610281578063ae951b2e14610289578063b38c43e314610291578063b888c479146102a4578063c5fa55a5146102ac57600080fd5b80634929e162146102265780635c19a95c1461022e578063626be5671461024157806367fc6dea1461024957806378e979251461025157600080fd5b806318369a2a1161010a57806318369a2a146101df5780632526d960146101e857806328b5030b146101f057806338af3eed146101f8578063427db3801461020b57806344f61ab71461021e57600080fd5b8062d89b33146101465780630357371d146101615780630f45cc81146101765780630fb5a6b4146101a157806310c48245146101b8575b600080fd5b60005415155b60405190151581526020015b60405180910390f35b61017461016f366004610a88565b6102bf565b005b600254610189906001600160a01b031681565b6040516001600160a01b039091168152602001610158565b6101aa60015481565b604051908152602001610158565b6101897f000000000000000000000000000000000000000000000000000000000000000081565b6101aa60055481565b610174610458565b6101aa61057a565b600354610189906001600160a01b031681565b600454610189906001600160a01b031681565b61014c6105b6565b6101746105e8565b61017461023c366004610ab2565b6105f3565b6101aa61067f565b6101aa6106f1565b6101aa60005481565b6101aa7f000000000000000000000000000000000000000000000000000000000000000081565b6101aa610728565b61014c61073f565b61017461029f366004610ab2565b61074f565b6101aa610824565b6101746102ba366004610ab2565b61083b565b6003546001600160a01b031633146102f25760405162461bcd60e51b81526004016102e990610ad4565b60405180910390fd5b6006546102fd61067f565b111561032f57600060065461031061067f565b61031a9190610b34565b90508060055461032a9190610b4b565b600555505b8061037c5760405162461bcd60e51b815260206004820181905260248201527f546f6b656e54696d656c6f636b3a206e6f20616d6f756e74206465736972656460448201526064016102e9565b6103846105b6565b6103d05760405162461bcd60e51b815260206004820152601f60248201527f546f6b656e54696d656c6f636b3a20436c696666206e6f74207061737365640060448201526064016102e9565b60006103da61057a565b90508082111561043e5760405162461bcd60e51b815260206004820152602960248201527f546f6b656e54696d656c6f636b3a206e6f7420656e6f7567682072656c656173604482015268656420746f6b656e7360b81b60648201526084016102e9565b61044883836108af565b5061045161067f565b6006555050565b60065461046361067f565b111561049557600060065461047661067f565b6104809190610b34565b9050806005546104909190610b4b565b600555505b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105175760405162461bcd60e51b815260206004820152602160248201527f546f6b656e54696d656c6f636b3a204f6e6c7920636c61776261636b41646d696044820152603760f91b60648201526084016102e9565b61051f6105b6565b1561054157600354610541906001600160a01b031661053c61057a565b6108af565b61056d7f000000000000000000000000000000000000000000000000000000000000000061053c61067f565b61057561067f565b600655565b6000806105856106f1565b905060006105986005548360015461096f565b905060006105a4610824565b6105ae9083610b34565b949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000006105e16106f1565b1015905090565b6105f13361099c565b565b6003546001600160a01b0316331461061d5760405162461bcd60e51b81526004016102e990610ad4565b6002546040516317066a5760e21b81526001600160a01b03838116600483015290911690635c19a95c90602401600060405180830381600087803b15801561066457600080fd5b505af1158015610678573d6000803e3d6000fd5b5050505050565b6002546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa1580156106c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ec9190610b63565b905090565b600080546106ff5750600090565b600154600080546107109042610b34565b905081811161071f5780610721565b815b9250505090565b60006107326106f1565b6001546106ec9190610b34565b6000610749610728565b15919050565b6003546001600160a01b031633146107795760405162461bcd60e51b81526004016102e990610ad4565b60065461078461067f565b11156107b657600060065461079761067f565b6107a19190610b34565b9050806005546107b19190610b4b565b600555505b6107be6105b6565b61080a5760405162461bcd60e51b815260206004820152601f60248201527f546f6b656e54696d656c6f636b3a20436c696666206e6f74207061737365640060448201526064016102e9565b6108168161053c61057a565b61081e61067f565b60065550565b600061082e61067f565b6005546106ec9190610b34565b6003546001600160a01b031633146108655760405162461bcd60e51b81526004016102e990610ad4565b600480546001600160a01b0319166001600160a01b0383169081179091556040517f636f16dafcc1e5b7ae44b2a7fd661757f160672716bb47a02c7d3f5108be49a090600090a250565b60025460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016020604051808303816000875af1158015610902573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109269190610b7c565b506003546040518281526001600160a01b038481169216907fcb54aad3bd772fcfe1bc124e01bd1a91a91c9d80126d8b3014c4d9e687d5ca489060200160405180910390a35050565b600081808461097e8188610b9e565b6109889190610b9e565b6109929190610bbd565b6105ae9190610bbd565b6004546001600160a01b03828116911614610a125760405162461bcd60e51b815260206004820152603060248201527f546f6b656e54696d656c6f636b3a2043616c6c6572206973206e6f742070656e60448201526f64696e672062656e656669636961727960801b60648201526084016102e9565b600380546001600160a01b0319166001600160a01b0383169081179091556040517fe356863d8c81d46ff30d41a6332e1d04d2fb6c0f043fa6554e3d1e1deae95a8a90600090a250600480546001600160a01b0319169055565b80356001600160a01b0381168114610a8357600080fd5b919050565b60008060408385031215610a9b57600080fd5b610aa483610a6c565b946020939093013593505050565b600060208284031215610ac457600080fd5b610acd82610a6c565b9392505050565b6020808252602a908201527f546f6b656e54696d656c6f636b3a2043616c6c6572206973206e6f7420612062604082015269656e656669636961727960b01b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b600082821015610b4657610b46610b1e565b500390565b60008219821115610b5e57610b5e610b1e565b500190565b600060208284031215610b7557600080fd5b5051919050565b600060208284031215610b8e57600080fd5b81518015158114610acd57600080fd5b6000816000190483118215151615610bb857610bb8610b1e565b500290565b600082610bda57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122013382079bd665f55e416f11d2bf14fd78b37a1ee85e646bcd387f3facb5ae39764736f6c634300080a0033

Verified Source Code Partial Match

Compiler: v0.8.10+commit.fc410830 EVM: london Optimization: Yes (200 runs)
Timed.sol 73 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

/// @title an abstract contract for timed events
/// @author Fei Protocol
abstract contract Timed {

    /// @notice the start timestamp of the timed period
    uint256 public startTime;

    /// @notice the duration of the timed period
    uint256 public duration;

    event DurationUpdate(uint256 oldDuration, uint256 newDuration);

    event TimerReset(uint256 startTime);

    constructor(uint256 _duration) {
        _setDuration(_duration);
    }

    modifier duringTime() {
        require(isTimeStarted(), "Timed: time not started");
        require(!isTimeEnded(), "Timed: time ended");
        _;
    }

    modifier afterTime() {
        require(isTimeEnded(), "Timed: time not ended");
        _;
    }

    /// @notice return true if time period has ended
    function isTimeEnded() public view returns (bool) {
        return remainingTime() == 0;
    }

    /// @notice number of seconds remaining until time is up
    /// @return remaining
    function remainingTime() public view returns (uint256) {
        return duration - timeSinceStart(); // duration always >= timeSinceStart which is on [0,d]
    }

    /// @notice number of seconds since contract was initialized
    /// @return timestamp
    /// @dev will be less than or equal to duration
    function timeSinceStart() public view returns (uint256) {
        if (!isTimeStarted()) {
            return 0; // uninitialized
        }
        uint256 _duration = duration;
        uint256 timePassed = block.timestamp - startTime; // block timestamp always >= startTime
        return timePassed > _duration ? _duration : timePassed;
    }

    function isTimeStarted() public view returns (bool) {
        return startTime != 0;
    }

    function _initTimed() internal {
        startTime = block.timestamp;
        
        emit TimerReset(block.timestamp);
    }

    function _setDuration(uint256 newDuration) internal {
        require(newDuration != 0, "Timed: zero duration");

        uint256 oldDuration = duration;
        duration = newDuration;
        emit DurationUpdate(oldDuration, newDuration);
    }
}
TokenTimelock.sol 155 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

// Inspired by OpenZeppelin TokenTimelock contract
// Reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/TokenTimelock.sol

import "../Timed.sol";
import "./ITokenTimelock.sol";

abstract contract TokenTimelock is ITokenTimelock, Timed {

    /// @notice ERC20 basic token contract being held in timelock
    IERC20 public override lockedToken;

    /// @notice beneficiary of tokens after they are released
    address public override beneficiary;

    /// @notice pending beneficiary appointed by current beneficiary
    address public override pendingBeneficiary;

    /// @notice initial balance of lockedToken
    uint256 public override initialBalance;

    uint256 internal lastBalance;

    /// @notice number of seconds before releasing is allowed
    uint256 public immutable cliffSeconds;

    address public immutable clawbackAdmin;

    constructor(
        address _beneficiary,
        uint256 _duration,
        uint256 _cliffSeconds,
        address _lockedToken,
        address _clawbackAdmin
    ) Timed(_duration) {
        require(_duration != 0, "TokenTimelock: duration is 0");
        require(
            _beneficiary != address(0),
            "TokenTimelock: Beneficiary must not be 0 address"
        );

        beneficiary = _beneficiary;
        _initTimed();

        _setLockedToken(_lockedToken);

        cliffSeconds = _cliffSeconds;

        clawbackAdmin = _clawbackAdmin;
    }

    // Prevents incoming LP tokens from messing up calculations
    modifier balanceCheck() {
        if (totalToken() > lastBalance) {
            uint256 delta = totalToken() - lastBalance;
            initialBalance = initialBalance + delta;
        }
        _;
        lastBalance = totalToken();
    }

    modifier onlyBeneficiary() {
        require(
            msg.sender == beneficiary,
            "TokenTimelock: Caller is not a beneficiary"
        );
        _;
    }

    /// @notice releases `amount` unlocked tokens to address `to`
    function release(address to, uint256 amount) external override onlyBeneficiary balanceCheck {
        require(amount != 0, "TokenTimelock: no amount desired");
        require(passedCliff(), "TokenTimelock: Cliff not passed");

        uint256 available = availableForRelease();
        require(amount <= available, "TokenTimelock: not enough released tokens");

        _release(to, amount);
    }

    /// @notice releases maximum unlocked tokens to address `to`
    function releaseMax(address to) external override onlyBeneficiary balanceCheck {
        require(passedCliff(), "TokenTimelock: Cliff not passed");
        _release(to, availableForRelease());
    }

    /// @notice the total amount of tokens held by timelock
    function totalToken() public view override virtual returns (uint256) {
        return lockedToken.balanceOf(address(this));
    }

    /// @notice amount of tokens released to beneficiary
    function alreadyReleasedAmount() public view override returns (uint256) {
        return initialBalance - totalToken();
    }

    /// @notice amount of held tokens unlocked and available for release
    function availableForRelease() public view override returns (uint256) {
        uint256 elapsed = timeSinceStart();

        uint256 totalAvailable = _proportionAvailable(initialBalance, elapsed, duration);
        uint256 netAvailable = totalAvailable - alreadyReleasedAmount();
        return netAvailable;
    }

    /// @notice current beneficiary can appoint new beneficiary, which must be accepted
    function setPendingBeneficiary(address _pendingBeneficiary)
        public
        override
        onlyBeneficiary
    {
        pendingBeneficiary = _pendingBeneficiary;
        emit PendingBeneficiaryUpdate(_pendingBeneficiary);
    }

    /// @notice pending beneficiary accepts new beneficiary
    function acceptBeneficiary() public override virtual {
        _setBeneficiary(msg.sender);
    }

    function clawback() public balanceCheck {
        require(msg.sender == clawbackAdmin, "TokenTimelock: Only clawbackAdmin");
        if (passedCliff()) {
            _release(beneficiary, availableForRelease());
        }
        _release(clawbackAdmin, totalToken());
    }

    function passedCliff() public view returns (bool) {
        return timeSinceStart() >= cliffSeconds;
    }

    function _proportionAvailable(uint256 initialBalance, uint256 elapsed, uint256 duration) internal pure virtual returns (uint256);

    function _setBeneficiary(address newBeneficiary) internal {
        require(
            newBeneficiary == pendingBeneficiary,
            "TokenTimelock: Caller is not pending beneficiary"
        );
        beneficiary = newBeneficiary;
        emit BeneficiaryUpdate(newBeneficiary);
        pendingBeneficiary = address(0);
    }

    function _setLockedToken(address tokenAddress) internal {
        lockedToken = IERC20(tokenAddress);
    }

    function _release(address to, uint256 amount) internal {
        lockedToken.transfer(to, amount);
        emit Release(beneficiary, to, amount);
    }
}
ITokenTimelock.sol 41 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title TokenTimelock interface
/// @author Fei Protocol
interface ITokenTimelock {
    // ----------- Events -----------

    event Release(address indexed _beneficiary, address indexed _recipient, uint256 _amount);
    event BeneficiaryUpdate(address indexed _beneficiary);
    event PendingBeneficiaryUpdate(address indexed _pendingBeneficiary);

    // ----------- State changing api -----------

    function release(address to, uint256 amount) external;

    function releaseMax(address to) external;

    function setPendingBeneficiary(address _pendingBeneficiary) external;

    function acceptBeneficiary() external;


    // ----------- Getters -----------

    function lockedToken() external view returns (IERC20);

    function beneficiary() external view returns (address);

    function pendingBeneficiary() external view returns (address);

    function initialBalance() external view returns (uint256);

    function availableForRelease() external view returns (uint256);

    function totalToken() external view returns(uint256);

    function alreadyReleasedAmount() external view returns (uint256);
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @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);
}
QuadraticTimelockedDelegator.sol 40 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../utils/timelock/QuadraticTokenTimelock.sol";

interface IVotingToken is IERC20 {
    function delegate(address delegatee) external;
}

/// @title a timelock for tokens allowing for bulk delegation
/// @author Fei Protocol
/// @notice allows the timelocked tokens to be delegated by the beneficiary while locked
contract QuadraticTimelockedDelegator is QuadraticTokenTimelock {
    /// @notice QuadraticTimelockedDelegator constructor
    /// @param _token the token address
    /// @param _beneficiary admin, and timelock beneficiary
    /// @param _duration duration of the token timelock window
    /// @param _cliff the seconds before first claim is allowed
    /// @param _clawbackAdmin the address which can trigger a clawback
    /// @param _startTime the unix epoch for starting timelock. Use 0 to start at deployment
    constructor(
        address _token,
        address _beneficiary,
        uint256 _duration,
        uint256 _cliff,
        address _clawbackAdmin,
        uint256 _startTime
    ) QuadraticTokenTimelock(_beneficiary, _duration, _token, _cliff, _clawbackAdmin, _startTime) {}

    /// @notice accept beneficiary role over timelocked TRIBE
    function acceptBeneficiary() public override {
        _setBeneficiary(msg.sender);
    }

    /// @notice delegate all held TRIBE to the `to` address
    function delegate(address to) public onlyBeneficiary {
        IVotingToken(address(lockedToken)).delegate(to);
    }
}
QuadraticTokenTimelock.sol 34 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "./TokenTimelock.sol";

contract QuadraticTokenTimelock is TokenTimelock {

    constructor (
        address _beneficiary,
        uint256 _duration,
        address _lockedToken,
        uint256 _cliffDuration,
        address _clawbackAdmin,
        uint256 _startTime
    ) TokenTimelock(
        _beneficiary, 
        _duration, 
        _cliffDuration,
        _lockedToken, 
        _clawbackAdmin
    ) {
        if (_startTime != 0) {
            startTime = _startTime;
        }
    }

    function _proportionAvailable(
        uint256 initialBalance,
        uint256 elapsed,
        uint256 duration
    ) internal pure override returns (uint256) {
        return initialBalance * elapsed * elapsed / duration / duration;
    }
}

Read Contract

alreadyReleasedAmount 0xb888c479 → uint256
availableForRelease 0x28b5030b → uint256
beneficiary 0x38af3eed → address
clawbackAdmin 0x10c48245 → address
cliffSeconds 0x8341ee72 → uint256
duration 0x0fb5a6b4 → uint256
initialBalance 0x18369a2a → uint256
isTimeEnded 0xae951b2e → bool
isTimeStarted 0x00d89b33 → bool
lockedToken 0x0f45cc81 → address
passedCliff 0x44f61ab7 → bool
pendingBeneficiary 0x427db380 → address
remainingTime 0xacc4bd08 → uint256
startTime 0x78e97925 → uint256
timeSinceStart 0x67fc6dea → uint256
totalToken 0x626be567 → uint256

Write Contract 6 functions

These functions modify contract state and require a wallet transaction to execute.

acceptBeneficiary 0x4929e162
No parameters
clawback 0x2526d960
No parameters
delegate 0x5c19a95c
address to
release 0x0357371d
address to
uint256 amount
releaseMax 0xb38c43e3
address to
setPendingBeneficiary 0xc5fa55a5
address _pendingBeneficiary

Recent Transactions

No transactions found for this address