Address Contract Partially Verified
Address
0x2E731a002260b15Fc90eC9AdAAd8070a8fa09A9a
Balance
0 ETH
Nonce
1
Code Size
3093 bytes
Creator
0xE2388f22...a510 at tx 0x4ab57538...953fa4
Indexed Transactions
0
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