Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xaa17a3F988644847881De48AF897CAB4E63dd62F
Balance 0 ETH
Nonce 2
Code Size 5798 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

5798 bytes
0x60806040523480156200001157600080fd5b5060043610620001cb5760003560e01c8063626be567116200010d578063ae951b2e11620000a3578063c5fa55a5116200007a578063c5fa55a5146200038b578063cf4f363014620003a2578063da8be86414620003ce578063fc0c546a14620003e557600080fd5b8063ae951b2e1462000360578063b38c43e3146200036a578063b888c479146200038157600080fd5b806380d04de811620000e457806380d04de814620003015780638341ee72146200030b5780639e88d77e1462000333578063acc4bd08146200035657600080fd5b8063626be56714620002e357806367fc6dea14620002ed57806378e9792514620002f757600080fd5b806318369a2a116200018357806338af3eed116200015a57806338af3eed14620002a7578063427db38014620002bb57806344f61ab714620002cf5780634929e16214620002d957600080fd5b806318369a2a14620002895780632526d960146200029357806328b5030b146200029d57600080fd5b8062d89b3314620001d0578063026e402b14620001eb5780630357371d14620002045780630f45cc81146200021b5780630fb5a6b4146200024857806310c482451462000261575b600080fd5b60005415155b60405190151581526020015b60405180910390f35b62000202620001fc36600462001008565b620003f9565b005b620002026200021536600462001008565b6200063f565b6002546200022f906001600160a01b031681565b6040516001600160a01b039091168152602001620001e2565b6200025260015481565b604051908152602001620001e2565b6200022f7f000000000000000000000000d7fb335fee222825ef5684fd9e9a924ab29e563f81565b6200025260055481565b62000202620007ec565b6200025262000925565b6003546200022f906001600160a01b031681565b6004546200022f906001600160a01b031681565b620001d662000969565b620002026200099d565b6200025262000a09565b6200025262000a29565b6200025260005481565b62000252600a5481565b620002527f000000000000000000000000000000000000000000000000000000000000000081565b620002526200034436600462001035565b60086020526000908152604090205481565b6200025262000a65565b620001d662000a80565b620002026200037b36600462001035565b62000a92565b6200025262000b7c565b620002026200039c36600462001035565b62000ba8565b6200022f620003b336600462001035565b6007602052600090815260409020546001600160a01b031681565b62000252620003df36600462001035565b62000c1f565b6009546200022f906001600160a01b031681565b6003546001600160a01b031633146200042f5760405162461bcd60e51b815260040162000426906200105a565b60405180910390fd5b6200043962000dc2565b8111156200049a5760405162461bcd60e51b815260206004820152602760248201527f54696d656c6f636b656444656c656761746f723a204e6f7420656e6f7567682060448201526662616c616e636560c81b606482015260840162000426565b6001600160a01b038281166000908152600760205260409020541615620004d557620004c68262000c1f565b620004d29082620010ba565b90505b6009546040516001600160a01b039091169060009084908390620004f99062000fe2565b6001600160a01b03928316815291166020820152604001604051809103906000f0801580156200052d573d6000803e3d6000fd5b506001600160a01b03858116600090815260076020908152604080832080546001600160a01b031916948616949094179093556008905220849055600a549091506200057b908490620010ba565b600a5560405163a9059cbb60e01b81526001600160a01b0382811660048301526024820185905283169063a9059cbb906044016020604051808303816000875af1158015620005ce573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005f49190620010d6565b50836001600160a01b03167fb0d234274aef7a61aa5a2eb44c23881ebf46a068cccbd413c978bcbd555fe17f846040516200063191815260200190565b60405180910390a250505050565b6003546001600160a01b031633146200066c5760405162461bcd60e51b815260040162000426906200105a565b6006546200067962000a09565b1115620006b25760006006546200068f62000a09565b6200069b9190620010fa565b905080600554620006ad9190620010ba565b600555505b80600003620007045760405162461bcd60e51b815260206004820181905260248201527f546f6b656e54696d656c6f636b3a206e6f20616d6f756e742064657369726564604482015260640162000426565b6200070e62000969565b6200075c5760405162461bcd60e51b815260206004820152601f60248201527f546f6b656e54696d656c6f636b3a20436c696666206e6f742070617373656400604482015260640162000426565b60006200076862000925565b905080821115620007ce5760405162461bcd60e51b815260206004820152602960248201527f546f6b656e54696d656c6f636b3a206e6f7420656e6f7567682072656c656173604482015268656420746f6b656e7360b81b606482015260840162000426565b620007da838362000e32565b50620007e562000a09565b6006555050565b600654620007f962000a09565b1115620008325760006006546200080f62000a09565b6200081b9190620010fa565b9050806005546200082d9190620010ba565b600555505b336001600160a01b037f000000000000000000000000d7fb335fee222825ef5684fd9e9a924ab29e563f1614620008b65760405162461bcd60e51b815260206004820152602160248201527f546f6b656e54696d656c6f636b3a204f6e6c7920636c61776261636b41646d696044820152603760f91b606482015260840162000426565b620008c062000969565b15620008e757600354620008e7906001600160a01b0316620008e162000925565b62000e32565b620009167f000000000000000000000000d7fb335fee222825ef5684fd9e9a924ab29e563f620008e162000a09565b6200092062000a09565b600655565b6000806200093262000a29565b90506000620009476005548360015462000ef5565b905060006200095562000b7c565b620009619083620010fa565b949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000006200099662000a29565b1015905090565b620009a83362000f10565b6009546040516317066a5760e21b81523360048201526001600160a01b0390911690635c19a95c90602401600060405180830381600087803b158015620009ee57600080fd5b505af115801562000a03573d6000803e3d6000fd5b50505050565b6000600a5462000a1862000dc2565b62000a249190620010ba565b905090565b6000805462000a385750600090565b6001546000805462000a4b9042620010fa565b905081811162000a5c578062000a5e565b815b9250505090565b600062000a7162000a29565b60015462000a249190620010fa565b600062000a8c62000a65565b15919050565b6003546001600160a01b0316331462000abf5760405162461bcd60e51b815260040162000426906200105a565b60065462000acc62000a09565b111562000b0557600060065462000ae262000a09565b62000aee9190620010fa565b90508060055462000b009190620010ba565b600555505b62000b0f62000969565b62000b5d5760405162461bcd60e51b815260206004820152601f60248201527f546f6b656e54696d656c6f636b3a20436c696666206e6f742070617373656400604482015260640162000426565b62000b6c81620008e162000925565b62000b7662000a09565b60065550565b600060055460001462000ba25762000b9362000a09565b60055462000a249190620010fa565b50600090565b6003546001600160a01b0316331462000bd55760405162461bcd60e51b815260040162000426906200105a565b600480546001600160a01b0319166001600160a01b0383169081179091556040517f636f16dafcc1e5b7ae44b2a7fd661757f160672716bb47a02c7d3f5108be49a090600090a250565b6003546000906001600160a01b0316331462000c4f5760405162461bcd60e51b815260040162000426906200105a565b6001600160a01b03808316600090815260076020526040902054168062000cd45760405162461bcd60e51b815260206004820152603260248201527f54696d656c6f636b656444656c656761746f723a2044656c656761746520636f6044820152711b9d1c9858dd081b9bdb995e1a5cdd195b9d60721b606482015260840162000426565b806001600160a01b0316633ccfd60b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801562000d1057600080fd5b505af115801562000d25573d6000803e3d6000fd5b505050506001600160a01b038316600090815260086020526040902054600a5462000d52908290620010fa565b600a556001600160a01b038416600081815260076020908152604080832080546001600160a01b0319169055600882528083209290925590518381527f17659a1d1f57d2e58b7063ee8b518b50d00bf3e5c0d8224b68ba865e4725a0b4910160405180910390a29150505b919050565b6009546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801562000e0c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000a24919062001110565b60025460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016020604051808303816000875af115801562000e86573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000eac9190620010d6565b506003546040518281526001600160a01b038481169216907fcb54aad3bd772fcfe1bc124e01bd1a91a91c9d80126d8b3014c4d9e687d5ca489060200160405180910390a35050565b60008162000f0484866200112a565b62000961919062001144565b6004546001600160a01b0382811691161462000f885760405162461bcd60e51b815260206004820152603060248201527f546f6b656e54696d656c6f636b3a2043616c6c6572206973206e6f742070656e60448201526f64696e672062656e656669636961727960801b606482015260840162000426565b600380546001600160a01b0319166001600160a01b0383169081179091556040517fe356863d8c81d46ff30d41a6332e1d04d2fb6c0f043fa6554e3d1e1deae95a8a90600090a250600480546001600160a01b0319169055565b610509806200116883390190565b80356001600160a01b038116811462000dbd57600080fd5b600080604083850312156200101c57600080fd5b620010278362000ff0565b946020939093013593505050565b6000602082840312156200104857600080fd5b620010538262000ff0565b9392505050565b6020808252602a908201527f546f6b656e54696d656c6f636b3a2043616c6c6572206973206e6f7420612062604082015269656e656669636961727960b01b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b80820180821115620010d057620010d0620010a4565b92915050565b600060208284031215620010e957600080fd5b815180151581146200105357600080fd5b81810381811115620010d057620010d0620010a4565b6000602082840312156200112357600080fd5b5051919050565b8082028115828204841417620010d057620010d0620010a4565b6000826200116257634e487b7160e01b600052601260045260246000fd5b50049056fe608060405234801561001057600080fd5b5060405161050938038061050983398101604081905261002f9161011b565b610038336100af565b600180546001600160a01b0319166001600160a01b038381169182179092556040516317066a5760e21b8152918416600483015290635c19a95c90602401600060405180830381600087803b15801561009057600080fd5b505af11580156100a4573d6000803e3d6000fd5b50505050505061014e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b038116811461011657600080fd5b919050565b6000806040838503121561012e57600080fd5b610137836100ff565b9150610145602084016100ff565b90509250929050565b6103ac8061015d6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633ccfd60b1461005c578063715018a6146100665780638da5cb5b1461006e578063f2fde38b14610097578063fc0c546a146100aa575b600080fd5b6100646100bd565b005b6100646101cf565b6000546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b6100646100a536600461030b565b6101e3565b60015461007b906001600160a01b031681565b6100c5610261565b6001546040516370a0823160e01b81523060048201526001600160a01b039091169060009082906370a0823190602401602060405180830381865afa158015610112573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610136919061033b565b9050816001600160a01b031663a9059cbb6101596000546001600160a01b031690565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018490526044016020604051808303816000875af11580156101a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101ca9190610354565b505050565b6101d7610261565b6101e160006102bb565b565b6101eb610261565b6001600160a01b0381166102555760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61025e816102bb565b50565b6000546001600160a01b031633146101e15760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161024c565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561031d57600080fd5b81356001600160a01b038116811461033457600080fd5b9392505050565b60006020828403121561034d57600080fd5b5051919050565b60006020828403121561036657600080fd5b8151801515811461033457600080fdfea2646970667358221220b022af11aa3da3b7757197f74df576a74eb70bfc6118728a8f6368251841e39e64736f6c63430008130033a2646970667358221220fcd9020014dbefb7288461cf6cc904a3bae080c0bb1edbb50e0ec450695726cc64736f6c63430008130033

Verified Source Code Partial Match

Compiler: v0.8.19+commit.7dd6d404 EVM: paris Optimization: Yes (200 runs)
TimelockedDelegator.sol 653 lines
// Sources flattened with hardhat v2.17.2 https://hardhat.org

// SPDX-License-Identifier: GPL-3.0-or-later AND MIT

// File lib/openzeppelin-contracts/contracts/utils/Context.sol

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
  function _msgSender() internal view virtual returns (address) {
    return msg.sender;
  }

  function _msgData() internal view virtual returns (bytes calldata) {
    return msg.data;
  }
}

// File lib/openzeppelin-contracts/contracts/access/Ownable.sol

// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
  address private _owner;

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev Initializes the contract setting the deployer as the initial owner.
   */
  constructor() {
    _transferOwnership(_msgSender());
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    _checkOwner();
    _;
  }

  /**
   * @dev Returns the address of the current owner.
   */
  function owner() public view virtual returns (address) {
    return _owner;
  }

  /**
   * @dev Throws if the sender is not the owner.
   */
  function _checkOwner() internal view virtual {
    require(owner() == _msgSender(), "Ownable: caller is not the owner");
  }

  /**
   * @dev Leaves the contract without owner. It will not be possible to call
   * `onlyOwner` functions. Can only be called by the current owner.
   *
   * NOTE: Renouncing ownership will leave the contract without an owner,
   * thereby disabling any functionality that is only available to the owner.
   */
  function renounceOwnership() public virtual onlyOwner {
    _transferOwnership(address(0));
  }

  /**
   * @dev Transfers ownership of the contract to a new account (`newOwner`).
   * Can only be called by the current owner.
   */
  function transferOwnership(address newOwner) public virtual onlyOwner {
    require(newOwner != address(0), "Ownable: new owner is the zero address");
    _transferOwnership(newOwner);
  }

  /**
   * @dev Transfers ownership of the contract to a new account (`newOwner`).
   * Internal function without access restriction.
   */
  function _transferOwnership(address newOwner) internal virtual {
    address oldOwner = _owner;
    _owner = newOwner;
    emit OwnershipTransferred(oldOwner, newOwner);
  }
}

// File lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol

// Original license: 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 src/timelocks/interface/ITimelockedDelegator.sol

// Original license: SPDX_License_Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;

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

/// @title TimelockedDelegator interface
/// @author Fei Protocol
/// @dev Modified from: https://github.com/fei-protocol/fei-protocol-core/blob/develop/contracts/timelocks/ITimelockedDelegator.sol
interface ITimelockedDelegator {
  // ----------- Events -----------

  event Delegate(address indexed _delegatee, uint256 _amount);

  event Undelegate(address indexed _delegatee, uint256 _amount);

  // ----------- Beneficiary only state changing api -----------

  function delegate(address delegatee, uint256 amount) external;

  function undelegate(address delegatee) external returns (uint256);

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

  function delegateContract(address delegatee) external view returns (address);

  function delegateAmount(address delegatee) external view returns (uint256);

  function totalDelegated() external view returns (uint256);

  function token() external view returns (IDelegatable);
}

// File src/timelocks/interface/ITokenTimelock.sol

// Original license: SPDX_License_Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;

/// @title TokenTimelock interface
/// @author Fei Protocol
/// @dev Modified from: https://github.com/fei-protocol/fei-protocol-core/blob/develop/contracts/timelocks/ITokenTimelock.sol
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);
}

// File src/timelocks/Timed.sol

// Original license: SPDX_License_Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;

/// @title an abstract contract for timed events
/// @author Fei Protocol
/// @dev Modified from: https://github.com/fei-protocol/fei-protocol-core/blob/develop/contracts/utils/Timed.sol
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);
  }
}

// File src/timelocks/TokenTimelock.sol

// Original license: SPDX_License_Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;

// Modified from: https://github.com/fei-protocol/fei-protocol-core/blob/develop/contracts/timelocks/TokenTimelock.sol

// Inspired by OpenZeppelin TokenTimelock contract
// Reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/TokenTimelock.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 virtual override returns (uint256) {
    return lockedToken.balanceOf(address(this));
  }

  /// @notice amount of tokens released to beneficiary
  function alreadyReleasedAmount() public view override returns (uint256) {
    return initialBalance == 0 ? 0 : 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 virtual override {
    _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);
  }
}

// File src/timelocks/LinearTokenTimelock.sol

// Original license: SPDX_License_Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;

/// Modified from: https://github.com/fei-protocol/fei-protocol-core/blob/develop/contracts/timelocks/LinearTokenTimelock.sol
/// @author Fei Protocol
contract LinearTokenTimelock 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) / duration;
  }
}

// File src/timelocks/TimelockedDelegator.sol

// Original license: SPDX_License_Identifier: GPL-3.0-or-later
pragma solidity 0.8.19;

/// @title a proxy delegate contract for token
/// @author Fei Protocol, modified by Connext. Fei reference:
///         https://github.com/fei-protocol/fei-protocol-core/blob/develop/contracts/timelocks/LinearTimelockedDelegator.sol
/// @dev https://eips.ethereum.org/EIPS/eip-4758 -> inclusion seems likely within
///      the next 4 years, so selfdestruct was removed from withdraw()
/// @dev
contract Delegatee is Ownable {
  IDelegatable public token;

  /// @notice Delegatee constructor
  /// @param _delegatee the address to delegate token to
  /// @param _token the delegatable token address
  constructor(address _delegatee, address _token) {
    token = IDelegatable(_token);
    token.delegate(_delegatee);
  }

  /// @notice send token back to timelock
  function withdraw() public onlyOwner {
    IDelegatable _token = token;
    uint256 balance = _token.balanceOf(address(this));
    _token.transfer(owner(), balance);
  }
}

/// @title a timelock for token allowing for sub-delegation
/// @author Fei Protocol
/// @notice allows the timelocked token to be delegated by the beneficiary while locked
contract TimelockedDelegator is ITimelockedDelegator, LinearTokenTimelock {
  /// @notice associated delegate proxy contract for a delegatee
  mapping(address => address) public override delegateContract;

  /// @notice associated delegated amount of token for a delegatee
  /// @dev Using as source of truth to prevent accounting errors by transferring to Delegate contracts
  mapping(address => uint256) public override delegateAmount;

  /// @notice the token contract
  IDelegatable public override token;

  /// @notice the total delegated amount of token
  uint256 public override totalDelegated;

  /// @notice Delegatee constructor
  /// @param _token the token address
  /// @param _beneficiary default delegate, admin, and timelock beneficiary
  /// @param _clawbackAdmin who can withdraw unclaimed tokens if timelock halted. use address(0) if there
  ///        shouldn't be clawbacks for this contract
  /// @param _cliffDuration cliff of unlock, in seconds. Use 0 for no cliff.
  /// @param _startTime start time of unlock period, in seconds. Use 0 for now.
  /// @param _duration duration of the token timelock window
  constructor(
    address _token,
    address _beneficiary,
    address _clawbackAdmin,
    uint256 _cliffDuration,
    uint256 _startTime,
    uint256 _duration
  ) LinearTokenTimelock(_beneficiary, _duration, _token, _cliffDuration, _clawbackAdmin, _startTime) {
    token = IDelegatable(_token);
    token.delegate(_beneficiary);
  }

  /// @notice delegate locked token to a delegatee
  /// @param delegatee the target address to delegate to
  /// @param amount the amount of token to delegate. Will increment existing delegated token
  function delegate(address delegatee, uint256 amount) public override onlyBeneficiary {
    require(amount <= _tokenBalance(), "TimelockedDelegator: Not enough balance");

    // withdraw and include an existing delegation
    if (delegateContract[delegatee] != address(0)) {
      amount = amount + undelegate(delegatee);
    }

    IDelegatable _token = token;
    address _delegateContract = address(new Delegatee(delegatee, address(_token)));
    delegateContract[delegatee] = _delegateContract;

    delegateAmount[delegatee] = amount;
    totalDelegated = totalDelegated + amount;

    _token.transfer(_delegateContract, amount);

    emit Delegate(delegatee, amount);
  }

  /// @notice return delegated token to the timelock
  /// @param delegatee the target address to undelegate from
  /// @return the amount of token returned
  function undelegate(address delegatee) public override onlyBeneficiary returns (uint256) {
    address _delegateContract = delegateContract[delegatee];
    require(_delegateContract != address(0), "TimelockedDelegator: Delegate contract nonexistent");

    Delegatee(_delegateContract).withdraw();

    uint256 amount = delegateAmount[delegatee];
    totalDelegated = totalDelegated - amount;

    delegateContract[delegatee] = address(0);
    delegateAmount[delegatee] = 0;

    emit Undelegate(delegatee, amount);

    return amount;
  }

  /// @notice calculate total token held plus delegated
  /// @dev used by LinearTokenTimelock to determine the released amount
  function totalToken() public view override returns (uint256) {
    return _tokenBalance() + totalDelegated;
  }

  /// @notice accept beneficiary role over timelocked token. Delegates all held (non-subdelegated) token to beneficiary
  function acceptBeneficiary() public override {
    _setBeneficiary(msg.sender);
    token.delegate(msg.sender);
  }

  function _tokenBalance() internal view returns (uint256) {
    return token.balanceOf(address(this));
  }
}

Read Contract

alreadyReleasedAmount 0xb888c479 → uint256
availableForRelease 0x28b5030b → uint256
beneficiary 0x38af3eed → address
clawbackAdmin 0x10c48245 → address
cliffSeconds 0x8341ee72 → uint256
delegateAmount 0x9e88d77e → uint256
delegateContract 0xcf4f3630 → address
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
token 0xfc0c546a → address
totalDelegated 0x80d04de8 → uint256
totalToken 0x626be567 → uint256

Write Contract 7 functions

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

acceptBeneficiary 0x4929e162
No parameters
clawback 0x2526d960
No parameters
delegate 0x026e402b
address delegatee
uint256 amount
release 0x0357371d
address to
uint256 amount
releaseMax 0xb38c43e3
address to
setPendingBeneficiary 0xc5fa55a5
address _pendingBeneficiary
undelegate 0xda8be864
address delegatee
returns: uint256

Recent Transactions

No transactions found for this address