Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x61012060 | 17595057 | 511 days ago | IN | 0 ETH | 0.03121866 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
SeasonV2
Compiler Version
v0.8.4+commit.c7e474f2
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
/* * Origin Protocol * https://originprotocol.com * * Released under the MIT license * SPDX-License-Identifier: MIT * https://github.com/OriginProtocol/nft-launchpad * * Copyright 2022 Origin Protocol, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity ^0.8.4; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {ISeason} from './ISeason.sol'; import {ISeries} from './Series.sol'; /** * @title Story Season staking contract * @notice Season logic contract to calculate rewards of stakers */ contract SeasonV2 is ISeason { struct User { bool exists; uint128 points; } struct SeasonMeta { bool bootstrapped; bool snapshotTaken; uint128 totalPoints; } // Final rewards values taken at end of season struct Snapshot { uint128 rewardETH; uint128 rewardOGN; } ISeries public immutable series; uint256 public immutable override startTime; uint256 public immutable override lockStartTime; uint256 public immutable override endTime; uint256 public immutable override claimEndTime; SeasonMeta public season; Snapshot public snapshot; mapping(address => User) public users; /** * @dev User has staked * @param userAddress - address of the user * @param amount - amount of OGN staked * @param points - points user received for this stake */ event Stake( address indexed userAddress, uint256 indexed amount, uint256 points ); /** * @dev User has unstaked * @param userAddress - address of the user */ event Unstake(address indexed userAddress); /** * @dev Staking period has ended and reward snapshot taken * @param totalRewardETH - Total ETH reward to split amongst stakers * @param totalRewardOGN - Total OGN reward to split amongst stakers */ event Finale(uint256 totalRewardETH, uint256 totalRewardOGN); /** * @param series_ - The Series staking and registry contract * @param startTime_ - Timestamp starting this season * @param lockStartTime_ - Timestamp at which new stakes are no longer accepted * @param endTime_ - Timestamp ending this season * @param claimEndTime_ - Timestamp at which users can no longer claim * profit share and rewards */ constructor( address series_, uint256 startTime_, uint256 lockStartTime_, uint256 endTime_, uint256 claimEndTime_ ) { series = ISeries(series_); startTime = startTime_; endTime = endTime_; lockStartTime = lockStartTime_; claimEndTime = claimEndTime_; require(startTime_ > block.timestamp, 'Season: Invalid startTime'); require(lockStartTime_ > startTime_, 'Season: Invalid lockStartTime'); require(endTime_ > lockStartTime_, 'Season: Invalid endTime'); require(claimEndTime_ > endTime_, 'Season: Invalid claimEndTime'); } // @dev only execute if sender is the Series contract modifier onlySeries() { require(msg.sender == address(series), 'Season: Not series contract'); _; } /// /// Externals /// /** * @dev Calculate the points a user would receive if they staked at a * specific block timestamp. * @param amount - The amount of OGN they would stake * @param blockStamp - The block timestamp to calculate for * @return points. 0 if out of season. */ function pointsInTime(uint256 amount, uint256 blockStamp) external view override returns (uint128) { return _pointsInTime(amount, blockStamp); } /** * @notice Total points for a user's stake * @param userAddress - address for which to return their points * @return total points */ function getPoints(address userAddress) external view override returns (uint128) { User memory user = _initMemUser(userAddress); return user.points; } /** * @notice Total points of all stakes * @return total points of all users' stakes */ function getTotalPoints() external view override returns (uint128) { if (season.bootstrapped) { return season.totalPoints; } else if (block.timestamp >= startTime) { // Any new stakes should trigger a bootstrap using these same // numbers. This is just a convenience for early season fetch // before new stakes. uint256 stakedOGN = series.totalSupply(); return _pointsInTime(stakedOGN, startTime); } return 0; } /** * @notice Return the expected rewards for a user. * @dev This will return zero values if outside the claim period. * * @param userAddress - Address for the user to calculate * @return ethShare - Amount of ETH a user would receive if claimed now * @return ognRewards - Amount of OGN a user would receive if claimed now */ function expectedRewards(address userAddress) external view override returns (uint256, uint256) { if ( block.timestamp < endTime || block.timestamp >= claimEndTime || season.totalPoints == 0 ) { return (0, 0); } User memory user = _initMemUser(userAddress); // Include the vault balance if it hasn't been collected address vault = series.vault(); uint256 ethBalance = season.snapshotTaken ? snapshot.rewardETH : vault.balance; uint256 ognBalance = season.snapshotTaken ? snapshot.rewardOGN : IERC20(series.ogn()).balanceOf(vault); uint256 ethShare = _calculateShare(user.points, ethBalance); uint256 ognRewards = _calculateShare(user.points, ognBalance); return (ethShare, ognRewards); } /** * @notice Stake OGN for a share of ETH profits and OGN rewards * @dev This may be called multiple times and the amount returned will * be for the user's totals, not the amount for this specific call. * * @param userAddress - the user staking their OGN * @param amount - the amount of (st)OGN being staked * @return total points received for the user's stake */ function stake(address userAddress, uint256 amount) external override onlySeries returns (uint128) { // Bootstrapping should have happened before we got here require(season.bootstrapped, 'Season: Season not bootstrapped.'); // calculate stake points uint128 points = _pointsInTime(amount, block.timestamp); User memory user = _initMemUser(userAddress); // Update season and user points season.totalPoints += points; user.points += points; // Store user (updates may have also come from _initMemUser()) users[userAddress] = user; emit Stake(userAddress, amount, user.points); return user.points; } /** * @notice Calculate and return ETH profit share and OGN rewards and zero * out the user's stake points. * * @param userAddress - the user staking their OGN * @return Amount of ETH profit share to pay the user * @return Amount of OGN rewards to pay the user */ function claim(address userAddress) external override onlySeries returns (uint256, uint256) { // Do not unstake and claim if not in claim period if (block.timestamp < endTime) { return (0, 0); } return _unstake(userAddress); } /** * @notice Calculate and return ETH profit share and OGN rewards and zero * out the user's stake points. * * @param userAddress - the user staking their OGN * @return Amount of ETH profit share to pay the user * @return Amount of OGN rewards to pay the user */ function unstake(address userAddress) external override onlySeries returns (uint256, uint256) { return _unstake(userAddress); } /** * @dev Set the initial total points, potentially rolling over stake * totals from the previous season. * @param initialSupply - The amount of staked OGN at the start of the * season. */ function bootstrap(uint256 initialSupply) external override onlySeries { require(!season.bootstrapped, 'Season: Already bootstrapped'); // Gas favorable update SeasonMeta memory meta = season; meta.bootstrapped = true; meta.totalPoints = _pointsInTime(initialSupply, startTime); season = meta; } /// /// Internals /// /** * @dev creates the final snapshot of rewards totals entitled to the * stakers of this period. This only happens once at the end of the * season. This constitutes the full amount of rewards for this * season. WARNING: This should not be called before endTime or a * snapshot will be taken and frozen too early, leaving rewards on * the table. */ function _snapshot() internal { address vault = series.vault(); Snapshot memory snap = Snapshot( uint128(vault.balance), uint128(IERC20(series.ogn()).balanceOf(vault)) ); emit Finale(snap.rewardETH, snap.rewardOGN); season.snapshotTaken = true; snapshot = snap; } /** * @dev Calculate a user's rewards amounts and clear stake points * * @param userAddress - The address of the user account * @return Amount of ETH entitlement * @return Amount of OGN entitlement */ function _unstake(address userAddress) internal returns (uint256, uint256) { if (!season.bootstrapped) { // Unable to calculate rewards because we aren't bootstrapped require(block.timestamp < endTime, 'Season: Not bootstrapped.'); // Nothing to unstake, no rewards to give. Season can still be // bootstrapped by Series with a new stake. return (0, 0); } User memory user = _initMemUser(userAddress); uint256 rewardETH = 0; uint256 rewardOGN = 0; // Only remove points from season totals if season has not ended to // preserve shares proportion calculation if (block.timestamp < endTime) { season.totalPoints -= user.points; } else { // Within claim period if (block.timestamp < claimEndTime) { (rewardETH, rewardOGN) = _calcRewards(user.points); } } // Zero out user points users[userAddress] = User(true, 0); emit Unstake(userAddress); return (rewardETH, rewardOGN); } /** * @dev Calculate the points a user would receive if they staked at a * specific block timestamp. * * @param amount - The amount of OGN they would stake * @param blockStamp - The block timestamp to calculate for * @return points */ function _pointsInTime(uint256 amount, uint256 blockStamp) internal view returns (uint128) { if (amount == 0 || blockStamp >= lockStartTime) { return 0; } // Pre-season stake points start at startTime uint256 effectiveStamp = blockStamp < startTime ? startTime : blockStamp; // Remainder ignored intentionally, only full days are counted uint256 stakeDays = (endTime - effectiveStamp) / 1 days; // Imprecise math intentional since resolution is only to 1 day uint256 points = amount * stakeDays; require(points < type(uint128).max, 'Season: Points overflow'); return uint128(points); } /** * @dev Claim and return amounts of ETH profit share and OGN rewards * entitled to the user. * * @param userPoints - a user's points to use for rewards calculation * @return userRewardETH - Amount of ETH share a user is entitled to * @return userRewardOGN - Amount of OGN rewards a user is entitled to */ function _calcRewards(uint256 userPoints) internal returns (uint256, uint256) { if (userPoints == 0) { return (0, 0); } // Get final rewards totals if (!season.snapshotTaken && block.timestamp >= endTime) { _snapshot(); } uint256 userRewardETH = _calculateShare( userPoints, uint256(snapshot.rewardETH) ); uint256 userRewardOGN = _calculateShare( userPoints, uint256(snapshot.rewardOGN) ); return (userRewardETH, userRewardOGN); } /** * @dev Initialize a user, potentially rolling over stakes from the * previous season. NOTE: This does not write to storage. * * @return initialized User */ function _initMemUser(address userAddress) internal view returns (User memory) { User memory user = users[userAddress]; ISeries staking = ISeries(series); // If the user is new, the user might be rolling over from a previous // season. Check for pre-existing stakes on Season. if (!user.exists) { uint256 latestStakeTime = staking.latestStakeTime(userAddress); // Do not assign points to a user that staked in a future season. // This could happen if a user stakes in the next season while // this one is in claim period. if (latestStakeTime > 0 && latestStakeTime <= startTime) { uint256 stakeBalance = staking.balanceOf(userAddress); user.points = _pointsInTime(stakeBalance, startTime); } // Mark the user as existing so we do not repeat this step user.exists = true; } return user; } /** * @dev Calculate the given user's share of a given value. * * @return share the user is currently entitled to of given rewards */ function _calculateShare(uint256 userPoints, uint256 totalRewards) internal view returns (uint256) { return (totalRewards * userPoints) / uint256(season.totalPoints); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal onlyInitializing { __Pausable_init_unchained(); } function __Pausable_init_unchained() internal onlyInitializing { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20PermitUpgradeable { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../extensions/IERC20PermitUpgradeable.sol"; import "../../../utils/AddressUpgradeable.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20Upgradeable { using AddressUpgradeable for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to * 0 before setting it to a non-zero value. */ function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20PermitUpgradeable token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title OUSD Governable Contract * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change * from owner to governor and renounce methods removed. Does not use * Context.sol like Ownable.sol does for simplification. * @author Origin Protocol Inc */ abstract contract Governable { // Storage position of the owner and pendingOwner of the contract // keccak256("OUSD.governor"); bytes32 private constant governorPosition = 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a; // keccak256("OUSD.pending.governor"); bytes32 private constant pendingGovernorPosition = 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db; // keccak256("OUSD.reentry.status"); bytes32 private constant reentryStatusPosition = 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535; // See OpenZeppelin ReentrancyGuard implementation uint256 constant _NOT_ENTERED = 1; uint256 constant _ENTERED = 2; event PendingGovernorshipTransfer( address indexed previousGovernor, address indexed newGovernor ); event GovernorshipTransferred( address indexed previousGovernor, address indexed newGovernor ); /** * @dev Initializes the contract setting the deployer as the initial Governor. */ constructor() { _setGovernor(msg.sender); emit GovernorshipTransferred(address(0), _governor()); } /** * @dev Returns the address of the current Governor. */ function governor() public view returns (address) { return _governor(); } /** * @dev Returns the address of the current Governor. */ function _governor() internal view returns (address governorOut) { bytes32 position = governorPosition; assembly { governorOut := sload(position) } } /** * @dev Returns the address of the pending Governor. */ function _pendingGovernor() internal view returns (address pendingGovernor) { bytes32 position = pendingGovernorPosition; assembly { pendingGovernor := sload(position) } } /** * @dev Throws if called by any account other than the Governor. */ modifier onlyGovernor() { require(isGovernor(), "Caller is not the Governor"); _; } /** * @dev Returns true if the caller is the current Governor. */ function isGovernor() public view returns (bool) { return msg.sender == _governor(); } function _setGovernor(address newGovernor) internal { bytes32 position = governorPosition; assembly { sstore(position, newGovernor) } } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { bytes32 position = reentryStatusPosition; uint256 _reentry_status; assembly { _reentry_status := sload(position) } // On the first call to nonReentrant, _notEntered will be true require(_reentry_status != _ENTERED, "Reentrant call"); // Any calls to nonReentrant after this point will fail assembly { sstore(position, _ENTERED) } _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) assembly { sstore(position, _NOT_ENTERED) } } function _setPendingGovernor(address newGovernor) internal { bytes32 position = pendingGovernorPosition; assembly { sstore(position, newGovernor) } } /** * @dev Transfers Governance of the contract to a new account (`newGovernor`). * Can only be called by the current Governor. Must be claimed for this to complete * @param _newGovernor Address of the new Governor */ function transferGovernance(address _newGovernor) external onlyGovernor { _setPendingGovernor(_newGovernor); emit PendingGovernorshipTransfer(_governor(), _newGovernor); } /** * @dev Claim Governance of the contract to a new account (`newGovernor`). * Can only be called by the new Governor. */ function claimGovernance() external { require( msg.sender == _pendingGovernor(), "Only the pending Governor can complete the claim" ); _changeGovernor(msg.sender); } /** * @dev Change Governance of the contract to a new account (`newGovernor`). * @param _newGovernor Address of the new Governor */ function _changeGovernor(address _newGovernor) internal { require(_newGovernor != address(0), "New Governor is address(0)"); emit GovernorshipTransferred(_governor(), _newGovernor); _setGovernor(_newGovernor); } }
/* * Origin Protocol * https://originprotocol.com * * Released under the MIT license * SPDX-License-Identifier: MIT * https://github.com/OriginProtocol/nft-launchpad * * Copyright 2022 Origin Protocol, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity ^0.8.4; import {IERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol'; import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'; import {PausableUpgradeable} from '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol'; import {SafeERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol'; import {Governable} from '../governance/Governable.sol'; interface IFeeVault { function controller() external view returns (address); function pause() external; function unpause() external; function sendETHRewards(address userAddress, uint256 amount) external returns (bool); function sendTokenRewards( address tokenAddress, address userAddress, uint256 amount ) external returns (bool); function recoverERC20( address tokenAddress, uint256 tokenAmount, address toAddress ) external returns (bool); function setController(address controllerAddress) external; } /** * @title Story FeeVault contract * @notice Contract to collect NFT sales profits and rewards to be distributed * to OGN stakers. */ contract FeeVault is Initializable, Governable, PausableUpgradeable, IFeeVault { using SafeERC20Upgradeable for IERC20Upgradeable; address public override controller; address private constant ASSET_ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // @dev Rewards have been sent to the season event RewardsSent( address indexed asset, address indexed toAddress, uint256 amount ); // @dev A new controller has been set event NewController(address controllerAddress); modifier onlyController() { require(_msgSender() == controller, 'FeeVault: Sender not controller'); _; } /** * @param controllerAddress - Address for the account that will receive the * rewards */ function initialize(address controllerAddress) external initializer { __Pausable_init(); // controller will probably be zero on initial deploy controller = controllerAddress; } /// /// Externals /// /** * @dev Send ETH rewards to a user. Can only be called by controller. * @param userAddress - address of the recipient of the ETH * @param amount - amount of ETH (in wei) */ function sendETHRewards(address userAddress, uint256 amount) external override whenNotPaused onlyController returns (bool) { require(userAddress != address(0), 'FeeVault: ETH to black hole'); require(amount > 0, 'FeeVault: Attempt to send 0 ETH'); emit RewardsSent(ASSET_ETH, userAddress, amount); // transfer() does not send enough gas for a delegate call to an // empty receive() function. (bool success, ) = userAddress.call{value: amount, gas: 2800}(''); // To align behavior with sendTokenRewards require(success, 'FeeVault: ETH transfer failed'); return success; } /** * @dev Send token rewards to a user. Can only be called by controller. * @param tokenAddress - address of the token to send * @param userAddress - address of the recipient of the tokens * @param amount - amount of the token to send */ function sendTokenRewards( address tokenAddress, address userAddress, uint256 amount ) external override whenNotPaused onlyController returns (bool) { require(userAddress != address(0), 'FeeVault: Token to black hole'); require(amount > 0, 'FeeVault: Attempt to send 0'); emit RewardsSent(tokenAddress, userAddress, amount); return _sendTokens(tokenAddress, userAddress, amount); } /** * @notice Recover ERC20 tokens sent to contract. This can only be called * by the governor. * @param tokenAddress - address of the token to recover * @param tokenAmount - amount of the token to recover * @param toAddress - address of the recipient of the tokens */ function recoverERC20( address tokenAddress, uint256 tokenAmount, address toAddress ) external override onlyGovernor whenNotPaused returns (bool) { return _sendTokens(tokenAddress, toAddress, tokenAmount); } /** * @notice Set series address */ function setController(address controllerAddress) external override onlyGovernor { emit NewController(controllerAddress); controller = controllerAddress; } /** * @notice Pause all funds movement functionality */ function pause() external override onlyGovernor { _pause(); } /** * @notice Pause all funds movement functionality */ function unpause() external override onlyGovernor { _unpause(); } // @dev Allow this contract to receive ETH receive() external payable {} /// /// Internals /// function _sendTokens( address tokenAddress, address toAddress, uint256 amount ) internal returns (bool) { IERC20Upgradeable(tokenAddress).safeTransfer(toAddress, amount); return true; } }
/* * Origin Protocol * https://originprotocol.com * * Released under the MIT license * SPDX-License-Identifier: MIT * https://github.com/OriginProtocol/nft-launchpad * * Copyright 2022 Origin Protocol, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity ^0.8.4; interface ISeason { function claimEndTime() external view returns (uint256); function lockStartTime() external view returns (uint256); function endTime() external view returns (uint256); function startTime() external view returns (uint256); function getTotalPoints() external view returns (uint128); function getPoints(address userAddress) external view returns (uint128); function expectedRewards(address userAddress) external view returns (uint256, uint256); function pointsInTime(uint256 amount, uint256 blockStamp) external view returns (uint128); function claim(address userAddress) external returns (uint256, uint256); function stake(address userAddress, uint256 amount) external returns (uint128); function unstake(address userAddress) external returns (uint256, uint256); function bootstrap(uint256 initialSupply) external; }
/* * Origin Protocol * https://originprotocol.com * * Released under the MIT license * SPDX-License-Identifier: MIT * https://github.com/OriginProtocol/nft-launchpad * * Copyright 2022 Origin Protocol, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ pragma solidity ^0.8.4; import {AddressUpgradeable as Address} from '@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol'; import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol'; import {IERC20Upgradeable as IERC20} from '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol'; import {Governable} from '../governance/Governable.sol'; import {IFeeVault} from './FeeVault.sol'; import {ISeason} from './ISeason.sol'; interface ISeries { function ogn() external view returns (address); function vault() external view returns (address); function currentClaimingIndex() external view returns (uint256); function currentStakingIndex() external view returns (uint256); function liveSeason() external view returns (uint256); function expectedClaimingSeason() external view returns (address); function expectedStakingSeason() external view returns (address); function latestStakeTime(address userAddress) external view returns (uint256); function balanceOf(address userAddress) external view returns (uint256); function totalSupply() external view returns (uint256); function claim() external returns (uint256, uint256); function stake(uint256 amount) external returns (uint256, uint256); function unstake() external returns (uint256); function popSeason() external; function pushSeason(address season) external; function bootstrapSeason(uint256 seasonIndex, uint256 totalStaked) external; } /** * @title Story Series staking contract * @notice Primary interaction OGN staking contract for Story profit sharing * and rewards. */ contract Series is Initializable, Governable, ISeries { address public override vault; address public override ogn; address[] public seasons; uint256 public override currentStakingIndex; uint256 public override currentClaimingIndex; uint256 private totalStakedOGN; mapping(address => uint256) private stakedOGN; mapping(address => uint256) private userLastStakingTime; /** * @dev A new season has been registered * @param number - The season ID (1-indexed) * @param season - The address of the new season */ event NewSeason(uint256 indexed number, address indexed season); /** * @dev A season has started * @param number - The season ID (1-indexed) * @param season - The address of the new season */ event SeasonStart(uint256 indexed number, address indexed season); /** * @dev A season has been cancelled and removed * @param season - The address of the new season */ event SeasonCancelled(address indexed season); // @dev only execute if there's an active season set modifier requireActiveSeason() { require(seasons.length > 0, 'Series: No active season'); _; } /** * @param ogn_ - Address for the OGN token * @param vault_ - Address for the FeeVault */ function initialize(address ogn_, address vault_) external initializer { require(ogn_ != address(0), 'Series: Zero address: OGN'); require(vault_ != address(0), 'Series: Zero address: Vault'); ogn = ogn_; vault = vault_; } /// /// Externals /// /** * @notice The current "live" season (earliest non-ended season) * @return index of the live season */ function liveSeason() external view override returns (uint256) { if (seasons.length <= 1) { return 0; } for (uint256 i = seasons.length; i > 0; i--) { uint256 idx = i - 1; if (block.timestamp >= ISeason(seasons[idx]).startTime()) { return idx; } } return currentStakingIndex; } /** * @notice The staking season, should stake() be called. This takes into * account currentStakingIndex potentially advancing. * @return address of the expected claiming season */ function expectedStakingSeason() external view override returns (address) { if (seasons.length < 1) { return address(0); } ISeason season = ISeason(seasons[currentStakingIndex]); if ( block.timestamp >= season.lockStartTime() && seasons.length > currentStakingIndex + 1 ) { return seasons[currentStakingIndex + 1]; } return seasons[currentStakingIndex]; } /** * @notice The claiming season, should claim/unstake be called. This * takes into account currentClaimingIndex potentially advancing. * @return address of the expected claiming season */ function expectedClaimingSeason() external view override returns (address) { if (seasons.length < 1) { return address(0); } ISeason season = ISeason(seasons[currentClaimingIndex]); if ( block.timestamp >= season.claimEndTime() && seasons.length > currentClaimingIndex + 1 ) { return seasons[currentClaimingIndex + 1]; } return seasons[currentClaimingIndex]; } /** * @notice Get the latest stake block timestamp for a user * @param userAddress - address for which to return their last stake time * @return timestamp for last stake time for a user (or 0 if none) */ function latestStakeTime(address userAddress) external view override returns (uint256) { return userLastStakingTime[userAddress]; } /** * @notice Total staked OGN for a user * @param userAddress - address for which to return their points * @return total OGN staked */ function balanceOf(address userAddress) external view override returns (uint256) { return stakedOGN[userAddress]; } /** * @notice Total staked OGN of all users * @return total OGN staked from all users */ function totalSupply() external view override returns (uint256) { return totalStakedOGN; } /** * @notice Set the address for the OGN token. * @dev other contracts reference this value as well * @param ogn_ - address for the contract */ function setOGN(address ogn_) external onlyGovernor { require(ogn_ != address(0), 'Series: Zero address: OGN'); ogn = ogn_; } /** * @notice Set the address for the FeeVault. * @dev other contracts reference this value as well * @param vault_ - address for the contract */ function setVault(address vault_) external onlyGovernor { require(vault_ != address(0), 'Series: Zero address: FeeVault'); vault = vault_; } /** * @notice Stake OGN for fee sharing and rewards. Users can call this * multiple times to add to their stake. This contract must be * approved to transfer the given amount of OGN from the user. * * @param amount - The amount of OGN to stake * @return total amount of OGN staked by the user * @return total points received for the user's entire stake for the * staking season */ function stake(uint256 amount) external override requireActiveSeason returns (uint256, uint256) { require(amount > 0, 'Series: No stake amount'); uint128 stakePoints; address userAddress = msg.sender; IERC20 token = IERC20(ogn); ISeason season = _acquireStakingSeason(); // Transfer OGN to Series require( token.transferFrom(userAddress, address(this), amount), 'Series: OGN transfer failed' ); // Record stake for the user and get their points total for return stakePoints = season.stake(userAddress, amount); // Update balances. This must occur after the stake() call to allow // for clean rollover. Otherwise, this new balance could be // considered historical and used as rollover on top of new amount. stakedOGN[userAddress] += amount; totalStakedOGN += amount; userLastStakingTime[userAddress] = block.timestamp; return (stakedOGN[userAddress], stakePoints); } /** * @notice Unstake previously staked OGN. This will unstake their full * OGN stake amount and pay out any rewards (if within a claim period) * * @return amount of OGN unstaked */ function unstake() external override requireActiveSeason returns (uint256) { address userAddress = msg.sender; uint256 amount = stakedOGN[userAddress]; ISeason claimSeason = _acquireClaimingSeason(); (uint256 rewardETH, uint256 rewardOGN) = claimSeason.unstake( userAddress ); // Make sure to unstake from staking season as well to zero-out user if (currentClaimingIndex < currentStakingIndex) { ISeason stakeSeason = ISeason(seasons[currentStakingIndex]); // Ignored return val because there can't be multiple seasons in // claim period at one time. This should return (0,0). stakeSeason.unstake(userAddress); } // Balance updates need to happen after unstake() calls to allow // rollover calculation to get a user's stake balance. stakedOGN[userAddress] = 0; totalStakedOGN -= amount; // Send rewards to user (if any) _transferRewards(userAddress, rewardETH, rewardOGN); // Send staked OGN back to user require( IERC20(ogn).transfer(userAddress, amount), 'Series: OGN transfer failed' ); return amount; } /** * @notice Claim profit share and OGN rewards. * * @return claimedETH - amount of ETH profit share claimed * @return claimedOGN - amount of OGN rewards claimed */ function claim() external override requireActiveSeason returns (uint256, uint256) { address userAddress = msg.sender; ISeason season = _acquireClaimingSeason(); (uint256 rewardETH, uint256 rewardOGN) = season.claim(userAddress); _transferRewards(userAddress, rewardETH, rewardOGN); return (rewardETH, rewardOGN); } /** * @notice Add a new season. It will be the last season in the sequence. * * @param season - address for the new season */ function pushSeason(address season) external override onlyGovernor { require(Address.isContract(season), 'Series: Season not a contract'); ISeason newSeason = ISeason(season); // If we have seasons to compare, do some sanity checks if (seasons.length > 0) { ISeason prevSeason = ISeason(seasons[seasons.length - 1]); // End time must be after claim period to prevent overlap of claim // periods require( newSeason.endTime() > prevSeason.claimEndTime(), 'Series: Invalid end time' ); // It's critical the start time begins after the previous season's // lock start time to avoid advancing early into the staking slot. // Since its end time is after the lock start time and seasons // probably shouldn't overlap for clarity sake, we check against // end time. require( newSeason.startTime() >= prevSeason.endTime(), 'Series: Invalid start time' ); } seasons.push(season); emit NewSeason(seasons.length - 1, season); if (seasons.length == 1) { ISeason(season).bootstrap(totalStakedOGN); emit SeasonStart(0, season); } } /** * @notice Remove the final scheduled season if it is not an active * staking season. */ function popSeason() external override onlyGovernor { require(seasons.length > 0, 'Series: No seasons to cancel'); require( currentStakingIndex < seasons.length - 1, 'Series: Season is active' ); address cancelled = seasons[seasons.length - 1]; // Remove the last element seasons.pop(); emit SeasonCancelled(cancelled); } /** * @notice Manually bootstrap a season. This should only be used in the * rare case a season receives no new stakes, so was never * bootstraped. * @param totalStaked - The amount of totalStakedOGN to send to * Season.bootstrap() */ function bootstrapSeason(uint256 seasonIndex, uint256 totalStaked) external override onlyGovernor { require(seasonIndex < seasons.length, 'Series: Season does not exist'); ISeason season = ISeason(seasons[seasonIndex]); require( block.timestamp >= season.lockStartTime(), 'Series: Not locked' ); season.bootstrap(totalStaked); } /// /// Internals /// /** * @dev Return the season to use for staking, advancing if necessary * @return staking season */ function _acquireStakingSeason() internal returns (ISeason) { ISeason season = ISeason(seasons[currentStakingIndex]); // Locked seasons can accept stakes but will not award points, // therefore the staker will receive no rewards. If we have another // Season available for (pre)staking, advance the index and use that // for staking operations. if ( block.timestamp >= season.lockStartTime() && seasons.length > currentStakingIndex + 1 ) { currentStakingIndex += 1; season = ISeason(seasons[currentStakingIndex]); season.bootstrap(totalStakedOGN); emit SeasonStart(currentStakingIndex, seasons[currentStakingIndex]); } return season; } /** * @dev Return the season to use for claiming, advancing if necessary * @return claiming season */ function _acquireClaimingSeason() internal returns (ISeason) { ISeason season = ISeason(seasons[currentClaimingIndex]); // If the claim period has ended, advance to the next season, if // available. if ( block.timestamp >= season.claimEndTime() && seasons.length > currentClaimingIndex + 1 ) { currentClaimingIndex += 1; season = ISeason(seasons[currentClaimingIndex]); } return season; } /** * @dev Transfer the given ETH and OGN to the given user from the vault * @param userAddress - Recipient of the rewards * @param rewardETH - Amount of ETH to transfer * @param rewardOGN - Amount of OGN to transfer */ function _transferRewards( address userAddress, uint256 rewardETH, uint256 rewardOGN ) internal { IFeeVault rewards = IFeeVault(vault); if (rewardETH > 0) { rewards.sendETHRewards(userAddress, rewardETH); } if (rewardOGN > 0) { rewards.sendTokenRewards(ogn, userAddress, rewardOGN); } } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"series_","type":"address"},{"internalType":"uint256","name":"startTime_","type":"uint256"},{"internalType":"uint256","name":"lockStartTime_","type":"uint256"},{"internalType":"uint256","name":"endTime_","type":"uint256"},{"internalType":"uint256","name":"claimEndTime_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalRewardETH","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRewardOGN","type":"uint256"}],"name":"Finale","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"points","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"}],"name":"Unstake","type":"event"},{"inputs":[{"internalType":"uint256","name":"initialSupply","type":"uint256"}],"name":"bootstrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"expectedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"getPoints","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalPoints","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"blockStamp","type":"uint256"}],"name":"pointsInTime","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"season","outputs":[{"internalType":"bool","name":"bootstrapped","type":"bool"},{"internalType":"bool","name":"snapshotTaken","type":"bool"},{"internalType":"uint128","name":"totalPoints","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"series","outputs":[{"internalType":"contract ISeries","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"snapshot","outputs":[{"internalType":"uint128","name":"rewardETH","type":"uint128"},{"internalType":"uint128","name":"rewardOGN","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"unstake","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint128","name":"points","type":"uint128"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101206040523480156200001257600080fd5b50604051620018ab380380620018ab8339810160408190526200003591620001af565b6001600160601b0319606086901b1660805260a084905260e082905260c0839052610100819052428411620000b15760405162461bcd60e51b815260206004820152601960248201527f536561736f6e3a20496e76616c696420737461727454696d650000000000000060448201526064015b60405180910390fd5b838311620001025760405162461bcd60e51b815260206004820152601d60248201527f536561736f6e3a20496e76616c6964206c6f636b537461727454696d650000006044820152606401620000a8565b828211620001535760405162461bcd60e51b815260206004820152601760248201527f536561736f6e3a20496e76616c696420656e6454696d650000000000000000006044820152606401620000a8565b818111620001a45760405162461bcd60e51b815260206004820152601c60248201527f536561736f6e3a20496e76616c696420636c61696d456e6454696d65000000006044820152606401620000a8565b505050505062000204565b600080600080600060a08688031215620001c7578081fd5b85516001600160a01b0381168114620001de578182fd5b602087015160408801516060890151608090990151929a91995097965090945092505050565b60805160601c60a05160c05160e051610100516115a962000302600039600081816101a1015281816106c20152610c96015260008181610137015281816103e30152818161069901528181610b9501528181610c1f01528181610e0801526110ec0152600081816101c80152610d790152600081816102020152818161044a0152818161050a0152818161061101528181610dad01528181610dda01528181610faf015261105c0152600081816103400152818161039b015281816104730152818161054501528181610720015281816107f60152818161096c01528181610b4901528181610eff0152818161116a015261122301526115a96000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80639711715a11610097578063eb9b5da111610066578063eb9b5da114610315578063ee204abb14610328578063f12d870f1461033b578063f2888dbb1461037a57600080fd5b80639711715a14610224578063a87430ba14610262578063adc9772e146102b4578063c50b0fb0146102c757600080fd5b806340d1d255116100d357806340d1d2551461019c57806362c7fa76146101c357806377e5006f146101ea57806378e97925146101fd57600080fd5b80631e83409a146101055780633197cbb614610132578063326bfcc1146101675780633686a39e14610187575b600080fd5b6101186101133660046113b1565b61038d565b604080519283526020830191909152015b60405180910390f35b6101597f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610129565b61016f610426565b6040516001600160801b039091168152602001610129565b61019a610195366004611414565b61053a565b005b6101597f000000000000000000000000000000000000000000000000000000000000000081565b6101597f000000000000000000000000000000000000000000000000000000000000000081565b6101186101f83660046113b1565b610694565b6101597f000000000000000000000000000000000000000000000000000000000000000081565b600154610242906001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610129565b6102956102703660046113b1565b60026020526000908152604090205460ff81169061010090046001600160801b031682565b6040805192151583526001600160801b03909116602083015201610129565b61016f6102c23660046113e9565b61095f565b6000546102ef9060ff808216916101008104909116906201000090046001600160801b031683565b60408051931515845291151560208401526001600160801b031690820152606001610129565b61016f610323366004611444565b610b11565b61016f6103363660046113b1565b610b24565b6103627f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610129565b6101186103883660046113b1565b610b3b565b600080336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146103e15760405162461bcd60e51b81526004016103d890611465565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000042101561041457506000905080915091565b61041d83610b86565b91509150915091565b6000805460ff161561044857506000546201000090046001600160801b031690565b7f000000000000000000000000000000000000000000000000000000000000000042106105345760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104ca57600080fd5b505afa1580156104de573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610502919061142c565b905061052e817f0000000000000000000000000000000000000000000000000000000000000000610d6d565b91505090565b50600090565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105825760405162461bcd60e51b81526004016103d890611465565b60005460ff16156105d55760405162461bcd60e51b815260206004820152601c60248201527f536561736f6e3a20416c726561647920626f6f7473747261707065640000000060448201526064016103d8565b6040805160608101825260005460ff610100820416151560208301526201000090046001600160801b03169181019190915260018152610635827f0000000000000000000000000000000000000000000000000000000000000000610d6d565b6001600160801b03166040820181905281516000805460209094015161ffff1990941691151561ff00191691909117610100931515939093029290921771ffffffffffffffffffffffffffffffff000019166201000090910217905550565b6000807f00000000000000000000000000000000000000000000000000000000000000004210806106e557507f00000000000000000000000000000000000000000000000000000000000000004210155b806106ff57506000546201000090046001600160801b0316155b1561070f57506000928392509050565b600061071a84610ea5565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561077757600080fd5b505afa15801561078b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107af91906113cd565b6000805491925090610100900460ff166107d357816001600160a01b0316316107e0565b6001546001600160801b03165b6000805491925090610100900460ff16610905577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663602bc0986040518163ffffffff1660e01b815260040160206040518083038186803b15801561084d57600080fd5b505afa158015610861573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088591906113cd565b6040516370a0823160e01b81526001600160a01b03858116600483015291909116906370a082319060240160206040518083038186803b1580156108c857600080fd5b505afa1580156108dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610900919061142c565b610919565b600154600160801b90046001600160801b03165b9050600061093485602001516001600160801b03168461109d565b9050600061094f86602001516001600160801b03168461109d565b9199919850909650505050505050565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109a95760405162461bcd60e51b81526004016103d890611465565b60005460ff166109fb5760405162461bcd60e51b815260206004820181905260248201527f536561736f6e3a20536561736f6e206e6f7420626f6f7473747261707065642e60448201526064016103d8565b6000610a078342610d6d565b90506000610a1485610ea5565b600080549192508391600290610a3a9084906201000090046001600160801b031661149c565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508181602001818151610a70919061149c565b6001600160801b039081169091526001600160a01b03871660008181526002602090815260409182902086518154888401519096166101008102610100600160881b0319921515929092166001600160881b031990971696909617179055905192835287935090917f5af417134f72a9d41143ace85b0a26dce6f550f894f2cbc1eeee8810603d91b6910160405180910390a3602001519150505b92915050565b6000610b1d8383610d6d565b9392505050565b600080610b3083610ea5565b602001519392505050565b600080336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104145760405162461bcd60e51b81526004016103d890611465565b60008054819060ff16610c0d577f00000000000000000000000000000000000000000000000000000000000000004210610c025760405162461bcd60e51b815260206004820152601960248201527f536561736f6e3a204e6f7420626f6f7473747261707065642e0000000000000060448201526064016103d8565b506000928392509050565b6000610c1884610ea5565b90506000807f0000000000000000000000000000000000000000000000000000000000000000421015610c9457602083015160008054600290610c6b9084906201000090046001600160801b0316611506565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550610cd8565b7f0000000000000000000000000000000000000000000000000000000000000000421015610cd857610cd283602001516001600160801b03166110c4565b90925090505b60408051808201825260018152600060208083018281526001600160a01b038b1680845260029092528483209351845491516001600160881b0319909216901515610100600160881b031916176101006001600160801b039092169190910217909255915190917fe5d648ba8f514a64a4104bf6922acc6e04ecab6464b46d696cf123c27079ddd791a2909590945092505050565b6000821580610d9c57507f00000000000000000000000000000000000000000000000000000000000000008210155b15610da957506000610b0b565b60007f00000000000000000000000000000000000000000000000000000000000000008310610dd85782610dfa565b7f00000000000000000000000000000000000000000000000000000000000000005b9050600062015180610e2c837f000000000000000000000000000000000000000000000000000000000000000061152e565b610e3691906114c7565b90506000610e4482876114e7565b90506001600160801b038110610e9c5760405162461bcd60e51b815260206004820152601760248201527f536561736f6e3a20506f696e7473206f766572666c6f7700000000000000000060448201526064016103d8565b95945050505050565b60408051808201909152600080825260208201526001600160a01b03821660009081526002602090815260409182902082518084019093525460ff811615158084526101009091046001600160801b0316918301919091527f000000000000000000000000000000000000000000000000000000000000000090611096576040516307161ecd60e01b81526001600160a01b038581166004830152600091908316906307161ecd9060240160206040518083038186803b158015610f6857600080fd5b505afa158015610f7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa0919061142c565b9050600081118015610fd257507f00000000000000000000000000000000000000000000000000000000000000008111155b15611090576040516370a0823160e01b81526001600160a01b038681166004830152600091908416906370a082319060240160206040518083038186803b15801561101c57600080fd5b505afa158015611030573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611054919061142c565b9050611080817f0000000000000000000000000000000000000000000000000000000000000000610d6d565b6001600160801b03166020850152505b50600182525b5092915050565b600080546201000090046001600160801b03166110ba84846114e7565b610b1d91906114c7565b600080826110d757506000928392509050565b600054610100900460ff1615801561110f57507f00000000000000000000000000000000000000000000000000000000000000004210155b1561111c5761111c611166565b6001546000906111369085906001600160801b031661109d565b60015490915060009061115a908690600160801b90046001600160801b031661109d565b91959194509092505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111c157600080fd5b505afa1580156111d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f991906113cd565b905060006040518060400160405280836001600160a01b0316316001600160801b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663602bc0986040518163ffffffff1660e01b815260040160206040518083038186803b15801561127a57600080fd5b505afa15801561128e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b291906113cd565b6040516370a0823160e01b81526001600160a01b03868116600483015291909116906370a082319060240160206040518083038186803b1580156112f557600080fd5b505afa158015611309573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132d919061142c565b6001600160801b0390811690915281516020808401516040805193851684529316908201529192507f524970edf64f36d519e36f01133bbed8f265248b90e244721ec383527b38e664910160405180910390a16000805461ff00191661010017905580516020909101516001600160801b03908116600160801b0291161760015550565b6000602082840312156113c2578081fd5b8135610b1d8161155b565b6000602082840312156113de578081fd5b8151610b1d8161155b565b600080604083850312156113fb578081fd5b82356114068161155b565b946020939093013593505050565b600060208284031215611425578081fd5b5035919050565b60006020828403121561143d578081fd5b5051919050565b60008060408385031215611456578182fd5b50508035926020909101359150565b6020808252601b908201527f536561736f6e3a204e6f742073657269657320636f6e74726163740000000000604082015260600190565b60006001600160801b038083168185168083038211156114be576114be611545565b01949350505050565b6000826114e257634e487b7160e01b81526012600452602481fd5b500490565b600081600019048311821515161561150157611501611545565b500290565b60006001600160801b038381169083168181101561152657611526611545565b039392505050565b60008282101561154057611540611545565b500390565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b038116811461157057600080fd5b5056fea264697066735822122094827ec37ba283c82c2effe0fea770fa0b0b3f8aef91003ddf09e7b05f97e94364736f6c63430008040033000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f0000000000000000000000000000000000000000000000000000000064a7558000000000000000000000000000000000000000000000000000000000651dfc80000000000000000000000000000000000000000000000000000000006545898000000000000000000000000000000000000000000000000000000000656d1680
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101005760003560e01c80639711715a11610097578063eb9b5da111610066578063eb9b5da114610315578063ee204abb14610328578063f12d870f1461033b578063f2888dbb1461037a57600080fd5b80639711715a14610224578063a87430ba14610262578063adc9772e146102b4578063c50b0fb0146102c757600080fd5b806340d1d255116100d357806340d1d2551461019c57806362c7fa76146101c357806377e5006f146101ea57806378e97925146101fd57600080fd5b80631e83409a146101055780633197cbb614610132578063326bfcc1146101675780633686a39e14610187575b600080fd5b6101186101133660046113b1565b61038d565b604080519283526020830191909152015b60405180910390f35b6101597f000000000000000000000000000000000000000000000000000000006545898081565b604051908152602001610129565b61016f610426565b6040516001600160801b039091168152602001610129565b61019a610195366004611414565b61053a565b005b6101597f00000000000000000000000000000000000000000000000000000000656d168081565b6101597f00000000000000000000000000000000000000000000000000000000651dfc8081565b6101186101f83660046113b1565b610694565b6101597f0000000000000000000000000000000000000000000000000000000064a7558081565b600154610242906001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610129565b6102956102703660046113b1565b60026020526000908152604090205460ff81169061010090046001600160801b031682565b6040805192151583526001600160801b03909116602083015201610129565b61016f6102c23660046113e9565b61095f565b6000546102ef9060ff808216916101008104909116906201000090046001600160801b031683565b60408051931515845291151560208401526001600160801b031690820152606001610129565b61016f610323366004611444565b610b11565b61016f6103363660046113b1565b610b24565b6103627f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f81565b6040516001600160a01b039091168152602001610129565b6101186103883660046113b1565b610b3b565b600080336001600160a01b037f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f16146103e15760405162461bcd60e51b81526004016103d890611465565b60405180910390fd5b7f000000000000000000000000000000000000000000000000000000006545898042101561041457506000905080915091565b61041d83610b86565b91509150915091565b6000805460ff161561044857506000546201000090046001600160801b031690565b7f0000000000000000000000000000000000000000000000000000000064a7558042106105345760007f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104ca57600080fd5b505afa1580156104de573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610502919061142c565b905061052e817f0000000000000000000000000000000000000000000000000000000064a75580610d6d565b91505090565b50600090565b336001600160a01b037f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f16146105825760405162461bcd60e51b81526004016103d890611465565b60005460ff16156105d55760405162461bcd60e51b815260206004820152601c60248201527f536561736f6e3a20416c726561647920626f6f7473747261707065640000000060448201526064016103d8565b6040805160608101825260005460ff610100820416151560208301526201000090046001600160801b03169181019190915260018152610635827f0000000000000000000000000000000000000000000000000000000064a75580610d6d565b6001600160801b03166040820181905281516000805460209094015161ffff1990941691151561ff00191691909117610100931515939093029290921771ffffffffffffffffffffffffffffffff000019166201000090910217905550565b6000807f00000000000000000000000000000000000000000000000000000000654589804210806106e557507f00000000000000000000000000000000000000000000000000000000656d16804210155b806106ff57506000546201000090046001600160801b0316155b1561070f57506000928392509050565b600061071a84610ea5565b905060007f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f6001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561077757600080fd5b505afa15801561078b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107af91906113cd565b6000805491925090610100900460ff166107d357816001600160a01b0316316107e0565b6001546001600160801b03165b6000805491925090610100900460ff16610905577f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f6001600160a01b031663602bc0986040518163ffffffff1660e01b815260040160206040518083038186803b15801561084d57600080fd5b505afa158015610861573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088591906113cd565b6040516370a0823160e01b81526001600160a01b03858116600483015291909116906370a082319060240160206040518083038186803b1580156108c857600080fd5b505afa1580156108dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610900919061142c565b610919565b600154600160801b90046001600160801b03165b9050600061093485602001516001600160801b03168461109d565b9050600061094f86602001516001600160801b03168461109d565b9199919850909650505050505050565b6000336001600160a01b037f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f16146109a95760405162461bcd60e51b81526004016103d890611465565b60005460ff166109fb5760405162461bcd60e51b815260206004820181905260248201527f536561736f6e3a20536561736f6e206e6f7420626f6f7473747261707065642e60448201526064016103d8565b6000610a078342610d6d565b90506000610a1485610ea5565b600080549192508391600290610a3a9084906201000090046001600160801b031661149c565b92506101000a8154816001600160801b0302191690836001600160801b031602179055508181602001818151610a70919061149c565b6001600160801b039081169091526001600160a01b03871660008181526002602090815260409182902086518154888401519096166101008102610100600160881b0319921515929092166001600160881b031990971696909617179055905192835287935090917f5af417134f72a9d41143ace85b0a26dce6f550f894f2cbc1eeee8810603d91b6910160405180910390a3602001519150505b92915050565b6000610b1d8383610d6d565b9392505050565b600080610b3083610ea5565b602001519392505050565b600080336001600160a01b037f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f16146104145760405162461bcd60e51b81526004016103d890611465565b60008054819060ff16610c0d577f00000000000000000000000000000000000000000000000000000000654589804210610c025760405162461bcd60e51b815260206004820152601960248201527f536561736f6e3a204e6f7420626f6f7473747261707065642e0000000000000060448201526064016103d8565b506000928392509050565b6000610c1884610ea5565b90506000807f0000000000000000000000000000000000000000000000000000000065458980421015610c9457602083015160008054600290610c6b9084906201000090046001600160801b0316611506565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550610cd8565b7f00000000000000000000000000000000000000000000000000000000656d1680421015610cd857610cd283602001516001600160801b03166110c4565b90925090505b60408051808201825260018152600060208083018281526001600160a01b038b1680845260029092528483209351845491516001600160881b0319909216901515610100600160881b031916176101006001600160801b039092169190910217909255915190917fe5d648ba8f514a64a4104bf6922acc6e04ecab6464b46d696cf123c27079ddd791a2909590945092505050565b6000821580610d9c57507f00000000000000000000000000000000000000000000000000000000651dfc808210155b15610da957506000610b0b565b60007f0000000000000000000000000000000000000000000000000000000064a755808310610dd85782610dfa565b7f0000000000000000000000000000000000000000000000000000000064a755805b9050600062015180610e2c837f000000000000000000000000000000000000000000000000000000006545898061152e565b610e3691906114c7565b90506000610e4482876114e7565b90506001600160801b038110610e9c5760405162461bcd60e51b815260206004820152601760248201527f536561736f6e3a20506f696e7473206f766572666c6f7700000000000000000060448201526064016103d8565b95945050505050565b60408051808201909152600080825260208201526001600160a01b03821660009081526002602090815260409182902082518084019093525460ff811615158084526101009091046001600160801b0316918301919091527f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f90611096576040516307161ecd60e01b81526001600160a01b038581166004830152600091908316906307161ecd9060240160206040518083038186803b158015610f6857600080fd5b505afa158015610f7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa0919061142c565b9050600081118015610fd257507f0000000000000000000000000000000000000000000000000000000064a755808111155b15611090576040516370a0823160e01b81526001600160a01b038681166004830152600091908416906370a082319060240160206040518083038186803b15801561101c57600080fd5b505afa158015611030573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611054919061142c565b9050611080817f0000000000000000000000000000000000000000000000000000000064a75580610d6d565b6001600160801b03166020850152505b50600182525b5092915050565b600080546201000090046001600160801b03166110ba84846114e7565b610b1d91906114c7565b600080826110d757506000928392509050565b600054610100900460ff1615801561110f57507f00000000000000000000000000000000000000000000000000000000654589804210155b1561111c5761111c611166565b6001546000906111369085906001600160801b031661109d565b60015490915060009061115a908690600160801b90046001600160801b031661109d565b91959194509092505050565b60007f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f6001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156111c157600080fd5b505afa1580156111d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f991906113cd565b905060006040518060400160405280836001600160a01b0316316001600160801b031681526020017f000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f6001600160a01b031663602bc0986040518163ffffffff1660e01b815260040160206040518083038186803b15801561127a57600080fd5b505afa15801561128e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b291906113cd565b6040516370a0823160e01b81526001600160a01b03868116600483015291909116906370a082319060240160206040518083038186803b1580156112f557600080fd5b505afa158015611309573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132d919061142c565b6001600160801b0390811690915281516020808401516040805193851684529316908201529192507f524970edf64f36d519e36f01133bbed8f265248b90e244721ec383527b38e664910160405180910390a16000805461ff00191661010017905580516020909101516001600160801b03908116600160801b0291161760015550565b6000602082840312156113c2578081fd5b8135610b1d8161155b565b6000602082840312156113de578081fd5b8151610b1d8161155b565b600080604083850312156113fb578081fd5b82356114068161155b565b946020939093013593505050565b600060208284031215611425578081fd5b5035919050565b60006020828403121561143d578081fd5b5051919050565b60008060408385031215611456578182fd5b50508035926020909101359150565b6020808252601b908201527f536561736f6e3a204e6f742073657269657320636f6e74726163740000000000604082015260600190565b60006001600160801b038083168185168083038211156114be576114be611545565b01949350505050565b6000826114e257634e487b7160e01b81526012600452602481fd5b500490565b600081600019048311821515161561150157611501611545565b500290565b60006001600160801b038381169083168181101561152657611526611545565b039392505050565b60008282101561154057611540611545565b500390565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b038116811461157057600080fd5b5056fea264697066735822122094827ec37ba283c82c2effe0fea770fa0b0b3f8aef91003ddf09e7b05f97e94364736f6c63430008040033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f0000000000000000000000000000000000000000000000000000000064a7558000000000000000000000000000000000000000000000000000000000651dfc80000000000000000000000000000000000000000000000000000000006545898000000000000000000000000000000000000000000000000000000000656d1680
-----Decoded View---------------
Arg [0] : series_ (address): 0xCcE8E784c777fb9435F89f4E45f8b7FC49f7669f
Arg [1] : startTime_ (uint256): 1688688000
Arg [2] : lockStartTime_ (uint256): 1696464000
Arg [3] : endTime_ (uint256): 1699056000
Arg [4] : claimEndTime_ (uint256): 1701648000
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000cce8e784c777fb9435f89f4e45f8b7fc49f7669f
Arg [1] : 0000000000000000000000000000000000000000000000000000000064a75580
Arg [2] : 00000000000000000000000000000000000000000000000000000000651dfc80
Arg [3] : 0000000000000000000000000000000000000000000000000000000065458980
Arg [4] : 00000000000000000000000000000000000000000000000000000000656d1680
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ 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.