ETH Price: $3,502.58 (+3.90%)
Gas: 4 Gwei

Contract

0x7bA57a77fE42B783c51ADaE8D8D6BF1365d75EEb
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Stake201902312024-06-28 12:37:352 days ago1719578255IN
0x7bA57a77...365d75EEb
0 ETH0.001061255.00064253
Stake201759032024-06-26 12:36:114 days ago1719405371IN
0x7bA57a77...365d75EEb
0 ETH0.001993245.20689459
Stake201733972024-06-26 4:13:114 days ago1719375191IN
0x7bA57a77...365d75EEb
0 ETH0.000753832.84932392
Stake201065022024-06-16 19:40:2314 days ago1718566823IN
0x7bA57a77...365d75EEb
0 ETH0.001194253.1639447
Stake201060402024-06-16 18:07:4714 days ago1718561267IN
0x7bA57a77...365d75EEb
0 ETH0.001513884.02419156
Stake201060182024-06-16 18:03:2314 days ago1718561003IN
0x7bA57a77...365d75EEb
0 ETH0.00169854.44234977
Stake201046362024-06-16 13:24:4714 days ago1718544287IN
0x7bA57a77...365d75EEb
0 ETH0.001851274.82542476
Stake200487382024-06-08 17:56:1122 days ago1717869371IN
0x7bA57a77...365d75EEb
0 ETH0.0022767610.82266988
Stake200487022024-06-08 17:48:4722 days ago1717868927IN
0x7bA57a77...365d75EEb
0 ETH0.0043659411.58121675
Stake200485372024-06-08 17:15:3522 days ago1717866935IN
0x7bA57a77...365d75EEb
0 ETH0.003240558.58068972
Stake200331852024-06-06 13:48:2324 days ago1717681703IN
0x7bA57a77...365d75EEb
0 ETH0.0062388524.09010804
Stake200330542024-06-06 13:21:5924 days ago1717680119IN
0x7bA57a77...365d75EEb
0 ETH0.0076796820.12711149
Stake200329572024-06-06 13:02:3524 days ago1717678955IN
0x7bA57a77...365d75EEb
0 ETH0.0040432619.05705992
Stake200248592024-06-05 9:54:1125 days ago1717581251IN
0x7bA57a77...365d75EEb
0 ETH0.0031789512.29882374
Stake200248412024-06-05 9:50:3525 days ago1717581035IN
0x7bA57a77...365d75EEb
0 ETH0.0028450311.57712384
Stake200201252024-06-04 18:03:2326 days ago1717524203IN
0x7bA57a77...365d75EEb
0 ETH0.0056223414.88081321
Stake200167902024-06-04 6:52:5926 days ago1717483979IN
0x7bA57a77...365d75EEb
0 ETH0.001369446.03759472
Stake200167832024-06-04 6:51:3526 days ago1717483895IN
0x7bA57a77...365d75EEb
0 ETH0.001558876.00498833
Stake200157782024-06-04 3:29:4726 days ago1717471787IN
0x7bA57a77...365d75EEb
0 ETH0.002381356.31122682
Stake200154222024-06-04 2:18:2326 days ago1717467503IN
0x7bA57a77...365d75EEb
0 ETH0.001641556.34295411
Stake200153502024-06-04 2:03:5926 days ago1717466639IN
0x7bA57a77...365d75EEb
0 ETH0.002914837.37752404
Stake200133262024-06-03 19:17:1127 days ago1717442231IN
0x7bA57a77...365d75EEb
0 ETH0.0026240510.45492893
0x61012060200029952024-06-02 8:38:4728 days ago1717317527IN
 Create: StakingManager
0 ETH0.019195628.14686346

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
200029952024-06-02 8:38:4728 days ago1717317527
0x7bA57a77...365d75EEb
 Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StakingManager

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 300 runs

Other Settings:
shanghai EvmVersion
File 1 of 15 : StakingManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import { Staker } from "./Staker.sol";

import { IHoldingManager } from "./interfaces/IHoldingManager.sol";
import { IIonPool } from "./interfaces/IIonPool.sol";
import { IStakingManager } from "./interfaces/IStakingManager.sol";
import { IStaker } from "./interfaces/IStaker.sol";

/**
 * @title StakingManager
 *
 * @notice Manages the distribution of rewards to early users of Jigsaw by facilitating staking of underlying assets.
 * @notice Staked assets are deposited into Ion's `Pool` Contract to generate yield and earn jPoints, redeemable for
 * governance $JIG tokens.
 * @notice For more information on Ion Protocol, visit https://ionprotocol.io.
 *
 * @dev This contract inherits functionalities from `Pausable`, `ReentrancyGuard`, and `Ownable2Step`.
 *
 * @author Hovooo (@hovooo)
 *
 * @custom:security-contact [email protected]
 */
contract StakingManager is IStakingManager, Pausable, ReentrancyGuard, Ownable2Step {
    using SafeERC20 for IERC20;

    /**
     * @notice Address of the Holding Manager contract.
     * @dev The Holding Manager is responsible for creating and managing user Holdings.
     */
    IHoldingManager public immutable override holdingManager;

    /**
     * @notice Address of the underlying asset used for staking.
     */
    address public immutable override underlyingAsset;

    /**
     * @notice Address of the reward token distributed for staking.
     */
    address public immutable override rewardToken;

    /**
     * @notice Address of the Ion Pool contract.
     */
    address public immutable override ionPool;

    /**
     * @notice Address of the Staker contract used for jPoints distribution.
     */
    address public immutable override staker;

    /**
     * @notice Represents the expiration date for the staking lockup period.
     * After this date, staked funds can be withdrawn.
     * @dev If not withdrawn will continue to generate rewards in `underlyingAsset` and,
     * if applicable, additional jPoints as long as staked.
     * @return The expiration date for the staking lockup period, in Unix timestamp format.
     */
    uint256 public override lockupExpirationDate;

    // --- Modifiers ---

    /**
     * @notice Modifier to check if the provided amount is valid.
     * @param _amount to be checked for validity.
     */
    modifier validAmount(uint256 _amount) {
        if (_amount == 0) revert InvalidAmount();
        _;
    }

    /**
     * @notice Modifier to check if the provided address is valid.
     * @param _address to be checked for validity.
     */
    modifier validAddress(address _address) {
        if (_address == address(0)) revert InvalidAddress();
        _;
    }

    // --- Constructor ---

    /**
     * @notice Constructor function for initializing the StakerManager contract.
     *
     * @param _initialOwner Address of the initial owner.
     * @param _holdingManager Address of the holding manager contract.
     * @param _underlyingAsset Address of the underlying asset used for staking.
     * @param _rewardToken Address of the reward token.
     * @param _ionPool Address of the IonPool Contract.
     * @param _rewardsDuration Duration of the rewards period.
     */
    constructor(
        address _initialOwner,
        address _holdingManager,
        address _underlyingAsset,
        address _rewardToken,
        address _ionPool,
        uint256 _rewardsDuration
    )
        Ownable(_initialOwner)
        validAddress(_holdingManager)
        validAddress(_underlyingAsset)
        validAddress(_rewardToken)
        validAddress(_ionPool)
        validAmount(_rewardsDuration)
    {
        holdingManager = IHoldingManager(_holdingManager);
        underlyingAsset = _underlyingAsset;
        rewardToken = _rewardToken;
        ionPool = _ionPool;
        staker = address(
            new Staker({
                _initialOwner: _initialOwner,
                _tokenIn: _underlyingAsset,
                _rewardToken: _rewardToken,
                _stakingManager: address(this),
                _rewardsDuration: _rewardsDuration
            })
        );
        lockupExpirationDate = block.timestamp + _rewardsDuration;
    }

    /**
     * @notice Stakes a specified amount of assets for the msg.sender.
     * @dev Initiates the staking operation by transferring the specified `_amount` from the user's wallet to the
     * contract, while simultaneously recording this deposit within the Jigsaw `Staker` Contract.
     *
     * @notice Requirements:
     * - The caller must have sufficient assets to stake.
     * - The Ion `Pool` Contract's supply cap should not exceed its limit after the user's stake operation.
     * - Prior approval is required for this contract to transfer assets on behalf of the user.
     *
     * @notice Effects:
     * - If the user does not have an existing holding, a new holding is created for the user.
     * - Supplies the specified amount of underlying asset to the Ion's `Pool` Contract to earn interest.
     * - Tracks the deposit in the `Staker` Contract to earn jPoints for staking.
     *
     * @notice Emits:
     * - `Staked` event indicating the staking action.
     *
     * @param _amount of assets to stake.
     */
    function stake(uint256 _amount) external override nonReentrant whenNotPaused validAmount(_amount) {
        // Create a holding for msg.sender if there is no holding associated with their address yet.
        address holding = holdingManager.getUserHolding(msg.sender);
        if (holding == address(0)) holding = holdingManager.createHolding(msg.sender);

        // Emit an event indicating the staking action.
        emit Staked(msg.sender, _amount);

        // Transfer assets from the user's wallet to this contract.
        IERC20(underlyingAsset).safeTransferFrom({ from: msg.sender, to: address(this), value: _amount });
        // Approve Ion Pool contract to spend the transferred assets.
        IERC20(underlyingAsset).safeIncreaseAllowance({ spender: ionPool, value: _amount });

        // Supply to the Ion Pool to earn interest on underlying asset.
        IIonPool(ionPool).supply({ user: holding, amount: _amount, proof: new bytes32[](0) });
        // Track deposit in Staker to earn jPoints for staking.
        IStaker(staker).deposit({ _user: holding, _amount: _amount });
    }

    /**
     * @notice Performs unstake operation.
     * @dev Initiates the withdrawal of staked assets by transferring all the deposited assets plus generated yield from
     * the Ion's `Pool` Contract and earned jPoint rewards from `Staker` Contract to the designated recipient `_to`.
     *
     * @notice Requirements:
     * - The `lockupExpirationDate` should have already expired.
     * - The caller must possess sufficient staked assets to fulfill the withdrawal.
     * - The `_to` address must be a valid Ethereum address.
     *
     * @notice Effects:
     * - Unstakes deposited and accrued underlying assets from Ion's `Pool` Contract.
     * - Withdraws jPoint rewards from `Staker` Contract.
     * - Withdraws Ion rewards from `Holding` through `HoldingManager` Contract.
     *
     *
     * @notice Emits:
     * - `Unstaked` event indicating the unstaking action.
     *
     * @param _to address to receive the unstaked assets.
     */
    function unstake(address _to) external override nonReentrant whenNotPaused validAddress(_to) {
        // Check if the lockup expiration date has passed.
        if (lockupExpirationDate > block.timestamp) revert PreLockupPeriodUnstaking();
        // Get the holding address of the caller.
        address holding = holdingManager.getUserHolding(msg.sender);

        // If the caller has no balance in the Ion Pool, revert with `NothingToWithdrawFromIon` error.
        uint256 ionPoolBalance = IIonPool(ionPool).balanceOf(holding);
        if (ionPoolBalance == 0) revert NothingToWithdrawFromIon(msg.sender);

        // Emit an event indicating the unstaking action.
        emit Unstaked(msg.sender, IStaker(staker).balanceOf(holding));

        // Unstake assets and withdraw rewards and transfer them to the specified address.
        holdingManager.unstake({ _holding: holding, _pool: ionPool, _to: _to, _amount: ionPoolBalance });
        IStaker(staker).exit({ _user: holding, _to: _to });
    }

    // --- Administration ---

    /**
     * @notice Triggers stopped state.
     */
    function pause() external override onlyOwner whenNotPaused {
        _pause();
    }

    /**
     * @notice Returns to normal state.
     */
    function unpause() external override onlyOwner whenPaused {
        _unpause();
    }

    /**
     * @notice Renounce ownership override to prevent accidental loss of contract ownership.
     * @dev This function ensures that the contract's ownership cannot be lost unintentionally.
     */
    function renounceOwnership() public pure override(IStakingManager, Ownable) {
        revert RenouncingOwnershipProhibited();
    }

    /**
     * @notice Allows the default admin role to set a new lockup expiration date.
     *
     * @notice Requirements:
     * - Caller must be `Owner`.
     * - `_newDate` should be different from `lockupExpirationDate`.
     *
     * @notice Effects:
     * - Sets `lockupExpirationDate` to `_newDate`.
     *
     *  @notice Emits:
     * - `LockupExpirationDateUpdated` event indicating that lockup expiration date has been updated.
     *
     * @param _newDate The new lockup expiration date to be set.
     */
    function setLockupExpirationDate(uint256 _newDate) external onlyOwner {
        // Sanity check that the `_newDate` is different from `lockupExpirationDate`.
        if (lockupExpirationDate == _newDate) revert SameValue();
        // Emit event indicating that the lockup expiration date has been updated.
        emit LockupExpirationDateUpdated(lockupExpirationDate, _newDate);
        // Update the state variable.
        lockupExpirationDate = _newDate;
    }

    // --- Getters ---

    /**
     * @notice Get the address of the holding associated with the user.
     * @param _user The address of the user.
     * @return the holding address.
     */
    function getUserHolding(address _user) external view override returns (address) {
        return holdingManager.getUserHolding(_user);
    }
}

File 2 of 15 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

File 3 of 15 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

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

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

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

File 4 of 15 : Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

import {Ownable} from "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

File 5 of 15 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.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 Pausable is Context {
    bool private _paused;

    /**
     * @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);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _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 {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @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());
    }
}

File 6 of 15 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @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 making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 7 of 15 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.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 SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @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(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (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(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, 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(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @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(IERC20 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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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(IERC20 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))) && address(token).code.length > 0;
    }
}

File 8 of 15 : Staker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { IStaker } from "./interfaces/IStaker.sol";

/**
 * @title Staker
 * @notice Staker is a contract responsible for distributing rewards in the form of jPoints to early users
 * of the Jigsaw protocol.
 * @notice This contract accepts wstETH tokens as `tokenIn` and distributes rewards in jPoints accordingly.
 * @notice It is not intended for direct use; interaction should be done through the `stakingManager` contract.
 *
 * @dev This contract inherits functionalities from `Ownable2Step`, `Pausable`, and `ReentrancyGuard`.
 *
 * @author Hovooo (@hovooo)
 */
contract Staker is IStaker, Ownable2Step, Pausable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    /**
     * @notice Address of the staking token.
     */
    address public immutable override tokenIn;

    /**
     * @notice Address of the reward token.
     */
    address public immutable override rewardToken;

    /**
     * @notice Address of the staking manager.
     */
    address public immutable override stakingManager;

    /**
     * @notice Timestamp indicating when the current reward distribution ends.
     */
    uint256 public override periodFinish = 0;

    /**
     * @notice Rate of rewards per second.
     */
    uint256 public override rewardRate = 0;

    /**
     * @notice Duration of current reward period.
     */
    uint256 public override rewardsDuration;

    /**
     * @notice Timestamp of the last update time.
     */
    uint256 public override lastUpdateTime;

    /**
     * @notice Stored rewards per token.
     */
    uint256 public override rewardPerTokenStored;

    /**
     * @notice Mapping of user addresses to the amount of rewards already paid to them.
     */
    mapping(address => uint256) public override userRewardPerTokenPaid;

    /**
     * @notice Mapping of user addresses to their accrued rewards.
     */
    mapping(address => uint256) public override rewards;

    /**
     * @notice Total supply limit of the staking token.
     */
    uint256 public constant totalSupplyLimit = 1e34;

    uint256 private _totalSupply;
    mapping(address => uint256) private _balances;

    // --- Modifiers ---

    /**
     * @notice Modifier to update the reward for a specified account.
     * @param account The account for which the reward needs to be updated.
     */
    modifier updateReward(address account) {
        rewardPerTokenStored = rewardPerToken();
        lastUpdateTime = lastTimeRewardApplicable();
        if (account != address(0)) {
            rewards[account] = earned(account);
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
        _;
    }

    /**
     * @notice Modifier to check if the provided address is valid.
     * @param _address to be checked for validity.
     */
    modifier validAddress(address _address) {
        if (_address == address(0)) revert InvalidAddress();
        _;
    }

    /**
     * @notice Modifier to check if the provided amount is valid.
     * @param _amount to be checked for validity.
     */
    modifier validAmount(uint256 _amount) {
        if (_amount == 0) revert InvalidAmount();
        _;
    }

    /**
     * @notice Modifier to restrict a function to be called only by the staking manager.
     * @notice Reverts the transaction if the caller is not the staking manager.
     */
    modifier onlyStakingManager() {
        if (msg.sender != stakingManager) revert UnauthorizedCaller();
        _;
    }

    // --- Constructor ---

    /**
     * @notice Constructor function for initializing the Staker contract.
     *
     * @param _initialOwner The initial owner of the contract
     * @param _tokenIn The address of the token to be staked
     * @param _rewardToken The address of the reward token
     * @param _stakingManager The address of the staking manager contract
     * @param _rewardsDuration The duration of the rewards period, in seconds
     */
    constructor(
        address _initialOwner,
        address _tokenIn,
        address _rewardToken,
        address _stakingManager,
        uint256 _rewardsDuration
    )
        Ownable(_initialOwner)
        validAddress(_tokenIn)
        validAddress(_rewardToken)
        validAddress(_stakingManager)
        validAmount(_rewardsDuration)
    {
        tokenIn = _tokenIn;
        rewardToken = _rewardToken;
        stakingManager = _stakingManager;
        rewardsDuration = _rewardsDuration;
        periodFinish = block.timestamp + rewardsDuration;
    }

    // -- Staker's operations  --

    /**
     * @notice Performs a deposit operation for `_user`.
     * @dev Updates participants' rewards.
     *
     * @param _user to deposit for.
     * @param _amount to deposit.
     */
    function deposit(
        address _user,
        uint256 _amount
    )
        external
        override
        onlyStakingManager
        whenNotPaused
        nonReentrant
        updateReward(_user)
        validAmount(_amount)
    {
        uint256 rewardBalance = IERC20(rewardToken).balanceOf(address(this));
        if (rewardBalance == 0) revert NoRewardsToDistribute();

        // Ensure that deposit operation will never surpass supply limit
        if (_totalSupply + _amount > totalSupplyLimit) revert DepositSurpassesSupplyLimit(_amount, totalSupplyLimit);
        _totalSupply += _amount;

        _balances[_user] += _amount;
        emit Staked(_user, _amount);
    }

    /**
     * @notice Withdraws specified `_amount` and claims rewards for the `_user`.
     * @dev This function enables the caller to exit the investment and claim their rewards.
     *
     *  @param _user to withdraw and claim for.
     *  @param _to address to which funds will be sent.
     */
    function exit(address _user, address _to) external override onlyStakingManager {
        withdraw(_user, _balances[_user]);

        if (rewards[_user] > 0) {
            claimRewards(_user, _to);
        }
    }

    /**
     * @notice Withdraws investment from staking.
     * @dev Updates participants' rewards.
     *
     * @param _user to withdraw for.
     * @param _amount to withdraw.
     */
    function withdraw(
        address _user,
        uint256 _amount
    )
        internal
        whenNotPaused
        nonReentrant
        updateReward(_user)
        validAmount(_amount)
    {
        _totalSupply -= _amount;
        _balances[_user] = _balances[_user] - _amount;
        emit Withdrawn(_user, _amount);
    }

    /**
     * @notice Claims the rewards for the caller.
     * @dev This function allows the caller to claim their earned rewards.
     *
     *  @param _user to claim rewards for.
     *  @param _to address to which rewards will be sent.
     */
    function claimRewards(address _user, address _to) internal nonReentrant updateReward(_user) {
        uint256 reward = rewards[_user];
        if (reward == 0) revert NothingToClaim();

        rewards[_user] = 0;
        emit RewardPaid(_user, reward);
        IERC20(rewardToken).safeTransfer(_to, reward);
    }

    // -- Administration --

    /**
     * @notice Sets the duration of each reward period.
     * @param _rewardsDuration The new rewards duration.
     */
    function setRewardsDuration(uint256 _rewardsDuration) external override onlyOwner {
        if (block.timestamp <= periodFinish) revert PreviousPeriodNotFinished(block.timestamp, periodFinish);
        emit RewardsDurationUpdated(rewardsDuration, _rewardsDuration);
        rewardsDuration = _rewardsDuration;
    }

    /**
     * @notice Adds more rewards to the contract.
     *
     * @dev Prior approval is required for this contract to transfer rewards from `_from` address.
     *
     * @param _from address to transfer rewards from.
     * @param _amount The amount of new rewards.
     */
    function addRewards(
        address _from,
        uint256 _amount
    )
        external
        override
        onlyOwner
        validAmount(_amount)
        updateReward(address(0))
    {
        // Transfer assets from the user's wallet to this contract.
        IERC20(rewardToken).safeTransferFrom({ from: _from, to: address(this), value: _amount });

        uint256 duration = rewardsDuration;
        if (duration == 0) revert ZeroRewardsDuration();
        if (block.timestamp >= periodFinish) {
            rewardRate = _amount / duration;
        } else {
            uint256 remaining = periodFinish - block.timestamp;
            uint256 leftover = remaining * rewardRate;
            rewardRate = (_amount + leftover) / duration;
        }

        if (rewardRate == 0) revert RewardAmountTooSmall();

        uint256 balance = IERC20(rewardToken).balanceOf(address(this));
        if (rewardRate > (balance / duration)) revert RewardRateTooBig();

        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp + duration;
        emit RewardAdded(_amount);
    }

    /**
     * @notice Triggers stopped state.
     */
    function pause() external override onlyOwner whenNotPaused {
        _pause();
    }

    /**
     * @notice Returns to normal state.
     */
    function unpause() external override onlyOwner whenPaused {
        _unpause();
    }

    /**
     * @notice Renounce ownership override to prevent accidental loss of contract ownership.
     * @dev This function ensures that the contract's ownership cannot be lost unintentionally.
     */
    function renounceOwnership() public pure override {
        revert RenouncingOwnershipProhibited();
    }

    // -- Getters --

    /**
     * @notice Returns the total supply of the staking token.
     */
    function totalSupply() external view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @notice Returns the total invested amount for an account.
     * @param _account The participant's address.
     */
    function balanceOf(address _account) external view override returns (uint256) {
        return _balances[_account];
    }

    /**
     * @notice Returns the last time rewards were applicable.
     */
    function lastTimeRewardApplicable() public view override returns (uint256) {
        return block.timestamp < periodFinish ? block.timestamp : periodFinish;
    }

    /**
     * @notice Returns rewards per token.
     */
    function rewardPerToken() public view override returns (uint256) {
        if (_totalSupply == 0) {
            return rewardPerTokenStored;
        }

        return
            rewardPerTokenStored + (((lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18) / _totalSupply);
    }

    /**
     * @notice Returns accrued rewards for an account.
     * @param _account The participant's address.
     */
    function earned(address _account) public view override returns (uint256) {
        return
            ((_balances[_account] * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e18) + rewards[_account];
    }

    /**
     * @notice Returns the reward amount for a specific time range.
     */
    function getRewardForDuration() external view override returns (uint256) {
        return rewardRate * rewardsDuration;
    }
}

File 9 of 15 : IHoldingManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title IHoldingManager interface
 * @dev Interface for the HoldingManager contract.
 */
interface IHoldingManager {
    // --- Errors ---

    /**
     * @notice The operation failed because provided address is invalid.
     */
    error InvalidAddress();

    /**
     * @notice The operation failed because the same address was provided for an update.
     */
    error SameAddress();

    /**
     * @notice The operation failed because renouncing default admin role is prohibited.
     */
    error RenouncingDefaultAdminRoleProhibited();

    /**
     * @notice The operation failed because user attempts an action that requires a holding contract associated with
     * their
     * address, but no holding contract is found.
     * @param user The address of the user who hasn't holding contract.
     */
    error MissingHoldingContractForUser(address user);

    /**
     * @notice The operation failed because the generic caller attempts to invoke a contract via a holding contract,
     * but the allowance for the invocation is not permitted.
     * @param caller The address of the generic caller attempting the invocation.
     */
    error InvocationNotAllowed(address caller);

    /**
     * @notice The operation failed because the generic call failed.
     * @param data returned by the failed call.
     */
    error InvocationFailed(bytes data);

    // --- Events ---
    /**
     * @notice Emitted when a holding is created for a participant.
     * @param user address for whom the holding was created.
     * @param holdingAddress address of the newly created holding.
     */
    event HoldingCreated(address indexed user, address indexed holdingAddress);

    /**
     * @notice emitted when the holding implementation reference is updated.
     *
     * @param oldReference address of the previous implementation reference.
     * @param newReference address of the new implementation reference.
     */
    event HoldingImplementationReferenceUpdated(address indexed oldReference, address indexed newReference);

    /**
     * @notice emitted when the allowance for invoking contracts via a holding contract is set.
     *
     * @param holding The address of the holding contract.
     * @param genericCaller The address of the generic caller.
     * @param callableContract The address of the contract that can be invoked.
     * @param oldAllowance of invocations allowed for the specified contract by the generic caller
     * via the holding contract.
     * @param newAllowance of invocations allowed for the specified contract by the generic caller
     * via the holding contract.
     */
    event InvocationAllowanceSet(
        address indexed holding,
        address indexed genericCaller,
        address indexed callableContract,
        uint256 oldAllowance,
        uint256 newAllowance
    );

    /**
     * Declaration of the Staking Manager role - privileged actor, allowed to call unstake function on Holdings.
     */
    function STAKING_MANAGER_ROLE() external view returns (bytes32);

    /**
     * Declaration of the Generic Caller role - privileged actor, allowed to perform low level calls on Holdings.
     */
    function GENERIC_CALLER_ROLE() external view returns (bytes32);

    /**
     * @notice Returns the address of the holding implementation reference.
     */
    function holdingImplementationReference() external view returns (address);

    /**
     * @notice Sets the allowance for a generic caller to invoke specified contracts on behalf of the user
     * through their holding contract.
     *
     * @notice Requirements:
     * - `_genericCaller` must be a valid address.
     * - `_callableContract` must be a valid address.
     * - The caller must have a holding contract associated with their address.
     * - The `_genericCaller` must have the `GENERIC_CALLER_ROLE`.
     *
     * @notice Effects:
     * - Updates `_invocationsAllowance` mapping.
     *
     * @notice Emits:
     * - Emits an `InvocationAllowanceSet` event upon successful execution.
     *
     * @param _genericCaller The address of the generic caller.
     * @param _callableContract The address of the contract to be invoked.
     * @param _invocationsAllowance The number of invocations allowed for the specified contract by the generic caller
     * via the holding contract.
     */
    function setInvocationAllowance(
        address _genericCaller,
        address _callableContract,
        uint256 _invocationsAllowance
    )
        external;

    /**
     * @notice Creates a new holding instance for the specified `user`.
     * @dev Clones a new holding contract instance using the reference implementation and associates it with the
     * `user`'s address and initializes the holding contract.
     * @dev Emits an event to signify the creation of the holding contract.
     *
     * @param _user The address of the user.
     *
     * @return holding The address of the newly created holding contract.
     */
    function createHolding(address _user) external returns (address holding);

    /**
     * @notice Unstake funds from a the specified Ion Protocol's `_pool` contract for `_holding`.
     *
     * @param _holding address to unstake for.
     * @param _pool address of Ion's pool.
     * @param _to The address where unstaked tokens will be sent.
     * @param _amount The amount of tokens to unstake.
     */
    function unstake(address _holding, address _pool, address _to, uint256 _amount) external;

    /**
     * @notice Allows a generic caller to invoke a function on a contract via a holding contract.
     * This function performs a generic call on the specified contract address using the provided call data.
     *
     * @notice Requirements:
     * - `_holding` must be a valid address representing the holding contract.
     * - `_contract` must be a valid address representing the target contract.
     * - The caller must have the `GENERIC_CALLER_ROLE`.
     * - The allowance for the caller on the specified `_contract` via `_holding` must be greater than 0.
     *
     *  @notice Effects:
     * - calls `genericCall` on the specified contract address using the provided call data.
     *
     * @param _holding The address of the holding contract where the call is invoked.
     * @param _contract The external contract being called by the holding contract.
     * @param _value The amount of Ether to transfer in the call.
     * @param _call The call data.
     *
     * @return success Indicates whether the call was successful or not.
     * @return result Data obtained from the external call.
     */
    function invokeHolding(
        address _holding,
        address _contract,
        uint256 _value,
        bytes calldata _call
    )
        external
        returns (bool success, bytes memory result);

    /**
     * @notice Allows the Default Admin to set a new address for `holdingImplementationReference` to be cloned from.
     *
     * @notice Requirements:
     * - Caller must have the `DEFAULT_ADMIN_ROLE`.
     * - `_newReference` should be valid address.
     *
     * @notice Emits:
     * - `HoldingImplementationReferenceUpdated` event indicating that holding implementation reference
     * has been updated.
     *
     * @param _newReference The address of the new implementation reference.
     */
    function setHoldingImplementationReference(address _newReference) external;

    /**
     * @notice Prevents the renouncement of the `DEFAULT_ADMIN_ROLE` by overriding `beginDefaultAdminTransfer` function
     * of the `AccessControlDefaultAdminRules` extension of the AccessControl.
     * @param newAdmin address of the new admin.
     */
    function beginDefaultAdminTransfer(address newAdmin) external;

    /**
     * @notice Get the address of the holding associated with the user.
     * @param _user The address of the user.
     * @return the holding address.
     */
    function getUserHolding(address _user) external view returns (address);

    /**
     * @notice Get the allowance for a generic caller to invoke contracts via a holding contract.
     *
     * @param _user The address of the user.
     * @param _genericCaller The address of the generic caller.
     * @param _callableContract The address of the contract to be invoked.
     * @return The number of invocations allowed for the specified contract by the generic caller
     * via the holding contract.
     */
    function getInvocationAllowance(
        address _user,
        address _genericCaller,
        address _callableContract
    )
        external
        view
        returns (uint256);
}

File 10 of 15 : IIonPool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IIonPool {
    /**
     * @dev Allows lenders to redeem their interest-bearing position for the
     * underlying asset. It is possible that dust amounts more of the position
     * are burned than the underlying received due to rounding.
     * @param receiverOfUnderlying the address to which the redeemed underlying
     * asset should be sent to.
     * @param amount of underlying to redeem.
     */
    function withdraw(address receiverOfUnderlying, uint256 amount) external;

    /**
     * @dev Allows lenders to deposit their underlying asset into the pool and
     * earn interest on it.
     * @param user the address to receive credit for the position.
     * @param amount of underlying asset to use to create the position.
     * @param proof merkle proof that the user is whitelisted.
     */
    function supply(address user, uint256 amount, bytes32[] calldata proof) external;

    /**
     * @dev Current token balance
     * @param user to get balance of
     */
    function balanceOf(address user) external view returns (uint256);
}

File 11 of 15 : IStakingManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IHoldingManager } from "./IHoldingManager.sol";

/**
 * @title IStakingManager interface
 * @notice Interface for the Staking Manager contract of the Jigsaw Protocol
 *
 */
interface IStakingManager {
    // --- Errors ---
    /**
     * @notice The operation failed because renouncing ownership is prohibited.
     */
    error RenouncingOwnershipProhibited();

    /**
     * @notice The operation failed because amount is invalid.
     */
    error InvalidAmount();

    /**
     * @notice The operation failed because the same value was provided for an update.
     */
    error SameValue();

    /**
     * @notice The operation failed because provided address is invalid.
     */
    error InvalidAddress();

    /**
     * @notice The operation failed because unstaking is not possible before lockup period ends.
     */
    error PreLockupPeriodUnstaking();

    /**
     * @notice The operation failed because caller's holding balance in the Ion Pool is zero.
     * @param caller address whose holding balance is zero.
     */
    error NothingToWithdrawFromIon(address caller);

    // --- Events ---
    /**
     * @notice Emitted when a participant stakes tokens.
     * @param user address of the participant who staked.
     * @param amount of the staked tokens.
     */
    event Staked(address indexed user, uint256 indexed amount);

    /**
     * @notice Emitted when a participant unstakes tokens.
     * @param user address of the participant who unstaked.
     * @param amount of the unstaked tokens.
     */
    event Unstaked(address indexed user, uint256 indexed amount);

    /**
     * @notice emitted when the expiration date of a lockup is updated.
     * @param oldDate The previous expiration date of the lockup.
     * @param newDate The new expiration date of the lockup.
     */
    event LockupExpirationDateUpdated(uint256 indexed oldDate, uint256 indexed newDate);

    /**
     * @notice Address of the Holding Manager contract.
     * @dev The Holding Manager is responsible for creating and managing user Holdings.
     */
    function holdingManager() external view returns (IHoldingManager);

    /**
     * @notice Address of the underlying asset used for staking.
     */
    function underlyingAsset() external view returns (address);

    /**
     * @notice Address of the reward token distributed for staking.
     */
    function rewardToken() external view returns (address);

    /**
     * @notice Address of the Ion Pool contract.
     */
    function ionPool() external view returns (address);

    /**
     * @notice Address of the Staker contract used for jPoints distribution.
     */
    function staker() external view returns (address);

    /**
     * @notice Represents the expiration date for the staking lockup period.
     * After this date, staked funds can be withdrawn.
     * @notice If not withdrawn will continue to generate rewards in `underlyingAsset` and,
     * if applicable, additional jPoints as long as staked.
     *
     * @return The expiration date for the staking lockup period, in Unix timestamp format.
     */
    function lockupExpirationDate() external view returns (uint256);

    /**
     * @notice Stakes a specified amount of assets for the msg.sender.
     * @dev Initiates the staking operation by transferring the specified `_amount` from the user's wallet to the
     * contract, while simultaneously recording this deposit within the Jigsaw `Staker` Contract.
     *
     * @notice Requirements:
     * - The caller must have sufficient assets to stake.
     * - The Ion `Pool` Contract's supply cap should not exceed its limit after the user's stake operation.
     * - Prior approval is required for this contract to transfer assets on behalf of the user.
     *
     * @notice Effects:
     * - If the user does not have an existing holding, a new holding is created for the user.
     * - Supplies the specified amount of underlying asset to the Ion's `Pool` Contract to earn interest.
     * - Tracks the deposit in the `Staker` Contract to earn jPoints for staking.
     *
     * @notice Emits:
     * - `Staked` event indicating the staking action.
     *
     * @param _amount of assets to stake.
     */
    function stake(uint256 _amount) external;

    /**
     * @notice Performs unstake operation.
     * @dev Initiates the withdrawal of staked assets by transferring all the deposited assets plus generated yield from
     * the Ion's `Pool` Contract and earned jPoint rewards from `Staker` Contract to the designated recipient `_to`.
     *
     * @notice Requirements:
     * - The `lockupExpirationDate` should have already expired.
     * - The caller must possess sufficient staked assets to fulfill the withdrawal.
     * - The `_to` address must be a valid Ethereum address.
     *
     * @notice Effects:
     * - Unstakes deposited and accrued underlying assets from Ion's `Pool` Contract.
     * - Withdraws jPoint rewards from `Staker` Contract.
     * - Withdraws Ion rewards from `Holding` through `HoldingManager` Contract.
     *
     *
     * @notice Emits:
     * - `Unstaked` event indicating the unstaking action.
     *
     * @param _to address to receive the unstaked assets.
     */
    function unstake(address _to) external;

    /**
     * @notice Triggers stopped state.
     */
    function pause() external;

    /**
     * @notice Returns to normal state.
     */
    function unpause() external;

    /**
     * @notice Renounce ownership override to prevent accidental loss of contract ownership.
     * @dev This function ensures that the contract's ownership cannot be lost unintentionally.
     */
    function renounceOwnership() external pure;

    /**
     * @notice Allows the default admin role to set a new lockup expiration date.
     *
     * @notice Requirements:
     * - Caller must be `Owner`.
     * - `_newDate` should be different from `lockupExpirationDate`.
     *
     * @notice Effects:
     * - Sets `lockupExpirationDate` to `_newDate`.
     *
     *  @notice Emits:
     * - `LockupExpirationDateUpdated` event indicating that lockup expiration date has been updated.
     *
     * @param _newDate The new lockup expiration date to be set.
     */
    function setLockupExpirationDate(uint256 _newDate) external;

    /**
     * @notice Get the address of the holding associated with the user.
     * @param _user The address of the user.
     * @return the holding address.
     */
    function getUserHolding(address _user) external view returns (address);
}

File 12 of 15 : IStaker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title Interface for Staker contract
interface IStaker {
    // --- Errors ---
    /**
     * @notice The operation failed because provided address is invalid.
     */
    error InvalidAddress();

    /**
     * @notice The operation failed because provided amount is invalid.
     */
    error InvalidAmount();

    /**
     * @notice The operation failed because caller was unauthorized for the action.
     */
    error UnauthorizedCaller();

    /**
     * @notice The operation failed because the previous rewards period must end first.
     * @param timestamp The current timestamp when the error occurred.
     * @param periodFinish The timestamp when the current rewards period is expected to end.
     */
    error PreviousPeriodNotFinished(uint256 timestamp, uint256 periodFinish);

    /**
     * @notice The operation failed because rewards duration is zero.
     */
    error ZeroRewardsDuration();

    /**
     * @notice The operation failed because reward rate was zero.
     * Caused by an insufficient amount of rewards provided.
     */
    error RewardAmountTooSmall();

    /**
     * @notice The operation failed because reward rate is too big.
     */
    error RewardRateTooBig();

    /**
     * @notice The operation failed because there were no rewards to distribute.
     */
    error NoRewardsToDistribute();

    /**
     * @notice The operation failed because deposit surpasses the supply limit.
     * @param _amount of tokens attempting to be deposited.
     * @param supplyLimit allowed for deposits.
     */
    error DepositSurpassesSupplyLimit(uint256 _amount, uint256 supplyLimit);

    /**
     * @notice The operation failed because user doesn't have rewards to claim.
     */
    error NothingToClaim();

    /**
     * @notice The operation failed because renouncing ownership is prohibited.
     */
    error RenouncingOwnershipProhibited();

    // --- Events ---

    /**
     * @notice Event emitted when the rewards duration is updated.
     * @param oldDuration The previous rewards duration.
     * @param newDuration The new rewards duration.
     */
    event RewardsDurationUpdated(uint256 indexed oldDuration, uint256 indexed newDuration);

    /**
     * @notice Event emitted when new rewards are added.
     * @param reward The amount of rewards added.
     */
    event RewardAdded(uint256 indexed reward);

    /**
     * @notice Event emitted when a participant deposits an amount.
     * @param user The address of the participant who made the deposit.
     * @param amount The amount that was deposited.
     */
    event Staked(address indexed user, uint256 indexed amount);

    /**
     * @notice Event emitted when a participant withdraws their stake.
     * @param user The address of the participant who withdrew their stake.
     * @param amount The amount that was withdrawn.
     */
    event Withdrawn(address indexed user, uint256 indexed amount);

    /**
     * @notice Event emitted when a participant claims their rewards.
     * @param user The address of the participant who claimed the rewards.
     * @param reward The amount of rewards that were claimed.
     */
    event RewardPaid(address indexed user, uint256 indexed reward);

    /**
     * @notice returns staking token address.
     */
    function tokenIn() external view returns (address);

    /**
     * @notice returns reward token address.
     */
    function rewardToken() external view returns (address);

    /**
     * @notice returns stakingManager address.
     */
    function stakingManager() external view returns (address);

    /**
     * @notice when current contract distribution ends (block timestamp + rewards duration).
     */
    function periodFinish() external view returns (uint256);

    /**
     * @notice rewards per second.
     */
    function rewardRate() external view returns (uint256);

    /**
     * @notice reward period.
     */
    function rewardsDuration() external view returns (uint256);

    /**
     * @notice last reward update timestamp.
     */
    function lastUpdateTime() external view returns (uint256);

    /**
     * @notice reward-token share.
     */
    function rewardPerTokenStored() external view returns (uint256);

    /**
     * @notice rewards paid to participants so far.
     */
    function userRewardPerTokenPaid(address participant) external view returns (uint256);

    /**
     * @notice accrued rewards per participant.
     */
    function rewards(address participant) external view returns (uint256);

    /**
     * @notice sets the new rewards duration.
     */
    function setRewardsDuration(uint256 _rewardsDuration) external;

    /**
     * @notice Adds more rewards to the contract.
     *
     * @dev Prior approval is required for this contract to transfer rewards from `_from` address.
     *
     * @param _from address to transfer rewards from.
     * @param _amount The amount of new rewards.
     */
    function addRewards(address _from, uint256 _amount) external;

    /**
     * @notice Triggers stopped state.
     */
    function pause() external;

    /**
     * @notice Returns to normal state.
     */
    function unpause() external;

    /**
     * @notice returns the total tokenIn supply.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice returns total invested amount for an account.
     * @param _account participant address
     */
    function balanceOf(address _account) external view returns (uint256);

    /**
     * @notice returns the last time rewards were applicable.
     */
    function lastTimeRewardApplicable() external view returns (uint256);

    /**
     * @notice returns rewards per tokenIn.
     */
    function rewardPerToken() external view returns (uint256);

    /**
     * @notice rewards accrued rewards for account.
     *  @param _account participant's address
     */
    function earned(address _account) external view returns (uint256);

    /**
     * @notice returns reward amount for a specific time range.
     */
    function getRewardForDuration() external view returns (uint256);

    /**
     * @notice Performs a deposit operation for `_user`.
     * @dev Updates participants' rewards.
     *
     * @param _user to deposit for.
     * @param _amount to deposit.
     */
    function deposit(address _user, uint256 _amount) external;

    /**
     * @notice Withdraws specified `_amount` and claims rewards for the `_user`.
     * @dev This function enables the caller to exit the investment and claim their rewards.
     *
     * @param _user to withdraw and claim for.
     * @param _to address to which funds will be sent.
     */
    function exit(address _user, address _to) external;
}

File 13 of 15 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 14 of 15 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @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].
     *
     * CAUTION: See Security Considerations above.
     */
    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);
}

File 15 of 15 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) 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 FailedInnerCall();
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 300
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"address","name":"_holdingManager","type":"address"},{"internalType":"address","name":"_underlyingAsset","type":"address"},{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"address","name":"_ionPool","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"NothingToWithdrawFromIon","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"PreLockupPeriodUnstaking","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RenouncingOwnershipProhibited","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SameValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"oldDate","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newDate","type":"uint256"}],"name":"LockupExpirationDateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserHolding","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"holdingManager","outputs":[{"internalType":"contract IHoldingManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ionPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockupExpirationDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newDate","type":"uint256"}],"name":"setLockupExpirationDate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"staker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlyingAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61012060405234801562000011575f80fd5b5060405162002c2e38038062002c2e83398101604081905262000034916200028b565b5f805460ff1916905560018055856001600160a01b0381166200007057604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6200007b81620001f2565b50846001600160a01b038116620000a55760405163e6c4247b60e01b815260040160405180910390fd5b846001600160a01b038116620000ce5760405163e6c4247b60e01b815260040160405180910390fd5b846001600160a01b038116620000f75760405163e6c4247b60e01b815260040160405180910390fd5b846001600160a01b038116620001205760405163e6c4247b60e01b815260040160405180910390fd5b84805f03620001425760405163162908e360e11b815260040160405180910390fd5b6001600160a01b03808b1660805289811660a05288811660c052871660e0526040518b908a908a9030908a90620001799062000261565b6001600160a01b039586168152938516602085015291841660408401529092166060820152608081019190915260a001604051809103905ff080158015620001c3573d5f803e3d5ffd5b506001600160a01b031661010052620001dd8642620002ff565b60045550620003259950505050505050505050565b600380546001600160a01b03191690556200020d8162000210565b50565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b61163a80620015f483390190565b80516001600160a01b038116811462000286575f80fd5b919050565b5f805f805f8060c08789031215620002a1575f80fd5b620002ac876200026f565b9550620002bc602088016200026f565b9450620002cc604088016200026f565b9350620002dc606088016200026f565b9250620002ec608088016200026f565b915060a087015190509295509295509295565b808201808211156200031f57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e0516101005161122f620003c55f395f8181610190015281816106bd015281816109310152610a9601525f8181610231015281816105cd015281816105fc0152818161088501526109e301525f6102a201525f81816101bf0152818161057601526105ab01525f818161011a015281816102e501528181610432015281816104c8015281816107f00152610a1a015261122f5ff3fe608060405234801561000f575f80fd5b5060043610610111575f3560e01c80638456cb591161009e578063e30c39781161006e578063e30c397814610253578063e5f9583114610264578063f2888dbb14610277578063f2fde38b1461028a578063f7c618c11461029d575f80fd5b80638456cb59146102005780638da5cb5b14610208578063a694fc3a14610219578063b51a4f241461022c575f80fd5b80635ebaf1db116100e45780635ebaf1db1461018b578063715018a6146101b25780637158da7c146101ba57806379ba5097146101e157806383cf7bcd146101e9575f80fd5b806319aeb94b146101155780633f28deca146101595780633f4ba83a1461016c5780635c975abb14610176575b5f80fd5b61013c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61013c6101673660046110cf565b6102c4565b610174610356565b005b5f5460ff166040519015158152602001610150565b61013c7f000000000000000000000000000000000000000000000000000000000000000081565b610174610370565b61013c7f000000000000000000000000000000000000000000000000000000000000000081565b610174610389565b6101f260045481565b604051908152602001610150565b6101746103d2565b6002546001600160a01b031661013c565b6101746102273660046110ea565b6103ea565b61013c7f000000000000000000000000000000000000000000000000000000000000000081565b6003546001600160a01b031661013c565b6101746102723660046110ea565b610721565b6101746102853660046110cf565b61077e565b6101746102983660046110cf565b610afb565b61013c7f000000000000000000000000000000000000000000000000000000000000000081565b604051631f946f6560e11b81526001600160a01b0382811660048301525f917f000000000000000000000000000000000000000000000000000000000000000090911690633f28deca90602401602060405180830381865afa15801561032c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103509190611101565b92915050565b61035e610b6c565b610366610b99565b61036e610bbb565b565b60405163830f4dd360e01b815260040160405180910390fd5b60035433906001600160a01b031681146103c65760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b6103cf81610c0c565b50565b6103da610b6c565b6103e2610c25565b61036e610c48565b6103f2610c84565b6103fa610c25565b80805f0361041b5760405163162908e360e11b815260040160405180910390fd5b604051631f946f6560e11b81523360048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633f28deca90602401602060405180830381865afa15801561047f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a39190611101565b90506001600160a01b03811661053d57604051630d62fb6160e41b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d62fb610906024016020604051808303815f875af1158015610516573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061053a9190611101565b90505b604051839033907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d905f90a361059e6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333086610cae565b6105f26001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000085610d1b565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016637ca5643d82855f60405190808252806020026020018201604052801561064d578160200160208202803683370190505b506040518463ffffffff1660e01b815260040161066c9392919061111c565b5f604051808303815f87803b158015610683575f80fd5b505af1158015610695573d5f803e3d5ffd5b50506040516311f9fbc960e21b81526001600160a01b038481166004830152602482018790527f00000000000000000000000000000000000000000000000000000000000000001692506347e7ef2491506044015f604051808303815f87803b158015610700575f80fd5b505af1158015610712573d5f803e3d5ffd5b5050505050506103cf60018055565b610729610b6c565b806004540361074b5760405163c23f6ccb60e01b815260040160405180910390fd5b6004546040518291907f5e756d56d50f99a044c06534300c7051941218b28582bc78c3f4e833b97d3163905f90a3600455565b610786610c84565b61078e610c25565b806001600160a01b0381166107b65760405163e6c4247b60e01b815260040160405180910390fd5b4260045411156107d957604051633d67494760e01b815260040160405180910390fd5b604051631f946f6560e11b81523360048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633f28deca90602401602060405180830381865afa15801561083d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108619190611101565b6040516370a0823160e01b81526001600160a01b0380831660048301529192505f917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156108ca573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108ee9190611178565b9050805f036109125760405163123ae41360e21b81523360048201526024016103bd565b6040516370a0823160e01b81526001600160a01b0383811660048301527f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610976573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061099a9190611178565b60405133907f0f5bb82176feb1b5e747e28471aa92156a04d9f3ab9f45f28e2d704232b93f75905f90a36040516333a45dbb60e11b81526001600160a01b0383811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301528581166044830152606482018390527f00000000000000000000000000000000000000000000000000000000000000001690636748bb76906084015f604051808303815f87803b158015610a5b575f80fd5b505af1158015610a6d573d5f803e3d5ffd5b5050604051635312a01d60e11b81526001600160a01b03858116600483015287811660248301527f000000000000000000000000000000000000000000000000000000000000000016925063a625403a91506044015f604051808303815f87803b158015610ad9575f80fd5b505af1158015610aeb573d5f803e3d5ffd5b505050505050506103cf60018055565b610b03610b6c565b600380546001600160a01b0383166001600160a01b03199091168117909155610b346002546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6002546001600160a01b0316331461036e5760405163118cdaa760e01b81523360048201526024016103bd565b5f5460ff1661036e57604051638dfc202b60e01b815260040160405180910390fd5b610bc3610b99565b5f805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600380546001600160a01b03191690556103cf81610da2565b5f5460ff161561036e5760405163d93c066560e01b815260040160405180910390fd5b610c50610c25565b5f805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610bef3390565b600260015403610ca757604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b6040516001600160a01b038481166024830152838116604483015260648201839052610d159186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050610df3565b50505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015610d68573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8c9190611178565b9050610d158484610d9d858561118f565b610e59565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f610e076001600160a01b03841683610ee8565b905080515f14158015610e2b575080806020019051810190610e2991906111ae565b155b15610e5457604051635274afe760e01b81526001600160a01b03841660048201526024016103bd565b505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610eaa8482610efc565b610d15576040516001600160a01b0384811660248301525f6044830152610ede91869182169063095ea7b390606401610ce3565b610d158482610df3565b6060610ef583835f610f9d565b9392505050565b5f805f846001600160a01b031684604051610f1791906111cd565b5f604051808303815f865af19150503d805f8114610f50576040519150601f19603f3d011682016040523d82523d5f602084013e610f55565b606091505b5091509150818015610f7f575080511580610f7f575080806020019051810190610f7f91906111ae565b8015610f9457505f856001600160a01b03163b115b95945050505050565b606081471015610fc25760405163cd78605960e01b81523060048201526024016103bd565b5f80856001600160a01b03168486604051610fdd91906111cd565b5f6040518083038185875af1925050503d805f8114611017576040519150601f19603f3d011682016040523d82523d5f602084013e61101c565b606091505b509150915061102c868383611036565b9695505050505050565b60608261104b5761104682611092565b610ef5565b815115801561106257506001600160a01b0384163b155b1561108b57604051639996b31560e01b81526001600160a01b03851660048201526024016103bd565b5080610ef5565b8051156110a25780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b03811681146103cf575f80fd5b5f602082840312156110df575f80fd5b8135610ef5816110bb565b5f602082840312156110fa575f80fd5b5035919050565b5f60208284031215611111575f80fd5b8151610ef5816110bb565b5f606082016001600160a01b0386168352602085818501526060604085015281855180845260808601915082870193505f5b8181101561116a5784518352938301939183019160010161114e565b509098975050505050505050565b5f60208284031215611188575f80fd5b5051919050565b8082018082111561035057634e487b7160e01b5f52601160045260245ffd5b5f602082840312156111be575f80fd5b81518015158114610ef5575f80fd5b5f82515f5b818110156111ec57602081860181015185830152016111d2565b505f92019182525091905056fea26469706673582212201b9e236fba4f67525e5076c8a43b1cb43d970752fef9f77b7ca4240df2bce4ee64736f6c6343000815003360e06040525f6003555f60045534801562000018575f80fd5b506040516200163a3803806200163a8339810160408190526200003b91620001e9565b846001600160a01b0381166200006a57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b620000758162000160565b506001805460ff60a01b19168155600255836001600160a01b038116620000af5760405163e6c4247b60e01b815260040160405180910390fd5b836001600160a01b038116620000d85760405163e6c4247b60e01b815260040160405180910390fd5b836001600160a01b038116620001015760405163e6c4247b60e01b815260040160405180910390fd5b83805f03620001235760405163162908e360e11b815260040160405180910390fd5b6001600160a01b0380891660805287811660a052861660c05260058590556200014d85426200024c565b6003555062000272975050505050505050565b600180546001600160a01b03191690556200017b816200017e565b50565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114620001e4575f80fd5b919050565b5f805f805f60a08688031215620001fe575f80fd5b6200020986620001cd565b94506200021960208701620001cd565b93506200022960408701620001cd565b92506200023960608701620001cd565b9150608086015190509295509295909350565b808201808211156200026c57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c051611373620002c75f395f8181610223015281816104ba01526107a501525f81816103e20152818161059a015281816108bd0152818161099a0152610f0f01525f6102a501526113735ff3fe608060405234801561000f575f80fd5b50600436106101c5575f3560e01c806380faa57d116100fe578063c8f33c911161009e578063e30c39781161006e578063e30c3978146103b0578063ebe2b12b146103c1578063f2fde38b146103ca578063f7c618c1146103dd575f80fd5b8063c8f33c9114610383578063cc1a378f1461038c578063cd3daf9d1461039f578063df136d65146103a7575f80fd5b80638da5cb5b116100d95780638da5cb5b14610337578063a625403a14610347578063a9fc507b1461035a578063bac21a221461036d575f80fd5b806380faa57d146103085780638456cb59146103105780638b87634714610318575f80fd5b806347e7ef241161016957806370a082311161014457806370a08231146102c7578063715018a6146102ef57806379ba5097146102f75780637b0a47ee146102ff575f80fd5b806347e7ef24146102705780635c975abb146102835780636daf390b146102a0575f80fd5b80631c1f78eb116101a45780631c1f78eb1461021657806322828cc21461021e578063386a95251461025d5780633f4ba83a14610266575f80fd5b80628cc262146101c95780630700037d146101ef57806318160ddd1461020e575b5f80fd5b6101dc6101d73660046111e2565b610404565b6040519081526020015b60405180910390f35b6101dc6101fd3660046111e2565b60096020525f908152604090205481565b600a546101dc565b6101dc61047f565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101e6565b6101dc60055481565b61026e610495565b005b61026e61027e3660046111fb565b6104af565b600154600160a01b900460ff1660405190151581526020016101e6565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6101dc6102d53660046111e2565b6001600160a01b03165f908152600b602052604090205490565b61026e61070f565b61026e610728565b6101dc60045481565b6101dc61076c565b61026e610782565b6101dc6103263660046111e2565b60086020525f908152604090205481565b5f546001600160a01b0316610245565b61026e610355366004611223565b61079a565b61026e6103683660046111fb565b61082d565b6101dc6e01ed09bead87c0378d8e640000000081565b6101dc60065481565b61026e61039a366004611254565b610a7f565b6101dc610ae9565b6101dc60075481565b6001546001600160a01b0316610245565b6101dc60035481565b61026e6103d83660046111e2565b610b48565b6102457f000000000000000000000000000000000000000000000000000000000000000081565b6001600160a01b0381165f908152600960209081526040808320546008909252822054670de0b6b3a764000090610439610ae9565b610443919061127f565b6001600160a01b0385165f908152600b60205260409020546104659190611292565b61046f91906112a9565b61047991906112c8565b92915050565b5f6005546004546104909190611292565b905090565b61049d610bb8565b6104a5610be4565b6104ad610c0e565b565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104f857604051635c427cd960e01b815260040160405180910390fd5b610500610c63565b610508610c8e565b81610511610ae9565b60075561051c61076c565b6006556001600160a01b038116156105625761053781610404565b6001600160a01b0382165f908152600960209081526040808320939093556007546008909152919020555b81805f036105835760405163162908e360e11b815260040160405180910390fd5b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156105e7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061060b91906112db565b9050805f0361062d57604051639788af9b60e01b815260040160405180910390fd5b6e01ed09bead87c0378d8e640000000084600a5461064b91906112c8565b111561068757604051639aea9b9f60e01b8152600481018590526e01ed09bead87c0378d8e640000000060248201526044015b60405180910390fd5b83600a5f82825461069891906112c8565b90915550506001600160a01b0385165f908152600b6020526040812080548692906106c49084906112c8565b909155505060405184906001600160a01b038716907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d905f90a350505061070b6001600255565b5050565b60405163830f4dd360e01b815260040160405180910390fd5b60015433906001600160a01b031681146107605760405163118cdaa760e01b81526001600160a01b038216600482015260240161067e565b61076981610cb6565b50565b5f600354421061077d575060035490565b504290565b61078a610bb8565b610792610c63565b6104ad610ccf565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107e357604051635c427cd960e01b815260040160405180910390fd5b6001600160a01b0382165f908152600b6020526040902054610806908390610d12565b6001600160a01b0382165f908152600960205260409020541561070b5761070b8282610e25565b610835610bb8565b80805f036108565760405163162908e360e11b815260040160405180910390fd5b5f61085f610ae9565b60075561086a61076c565b6006556001600160a01b038116156108b05761088581610404565b6001600160a01b0382165f908152600960209081526040808320939093556007546008909152919020555b6108e56001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016853086610f42565b6005545f8190036109095760405163814bcc1360e01b815260040160405180910390fd5b60035442106109245761091c81856112a9565b600455610961565b5f42600354610933919061127f565b90505f600454826109449190611292565b90508261095182886112c8565b61095b91906112a9565b60045550505b6004545f0361098357604051632d20592b60e21b815260040160405180910390fd5b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156109e7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a0b91906112db565b9050610a1782826112a9565b6004541115610a3957604051632525250360e21b815260040160405180910390fd5b426006819055610a4a9083906112c8565b60035560405185907fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d905f90a2505050505050565b610a87610bb8565b6003544211610ab65760035460405163bdbc226b60e01b8152426004820152602481019190915260440161067e565b6005546040518291907fd20a04eb2807bde8cbdf16ef27a46d94a3162d81818f1781c0fe4ed9194ca391905f90a3600555565b5f600a545f03610afa575060075490565b600a54600454600654610b0b61076c565b610b15919061127f565b610b1f9190611292565b610b3190670de0b6b3a7640000611292565b610b3b91906112a9565b60075461049091906112c8565b610b50610bb8565b600180546001600160a01b0383166001600160a01b03199091168117909155610b805f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f546001600160a01b031633146104ad5760405163118cdaa760e01b815233600482015260240161067e565b600154600160a01b900460ff166104ad57604051638dfc202b60e01b815260040160405180910390fd5b610c16610be4565b6001805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600154600160a01b900460ff16156104ad5760405163d93c066560e01b815260040160405180910390fd5b6002805403610cb057604051633ee5aeb560e01b815260040160405180910390fd5b60028055565b600180546001600160a01b031916905561076981610faf565b610cd7610c63565b6001805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610c463390565b610d1a610c63565b610d22610c8e565b81610d2b610ae9565b600755610d3661076c565b6006556001600160a01b03811615610d7c57610d5181610404565b6001600160a01b0382165f908152600960209081526040808320939093556007546008909152919020555b81805f03610d9d5760405163162908e360e11b815260040160405180910390fd5b82600a5f828254610dae919061127f565b90915550506001600160a01b0384165f908152600b6020526040902054610dd690849061127f565b6001600160a01b0385165f818152600b602052604080822093909355915185927f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d591a3505061070b6001600255565b610e2d610c8e565b81610e36610ae9565b600755610e4161076c565b6006556001600160a01b03811615610e8757610e5c81610404565b6001600160a01b0382165f908152600960209081526040808320939093556007546008909152919020555b6001600160a01b0383165f9081526009602052604081205490819003610ec0576040516312d37ee560e31b815260040160405180910390fd5b6001600160a01b0384165f81815260096020526040808220829055518392917fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e048691a3610f366001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168483610ffe565b505061070b6001600255565b6040516001600160a01b038481166024830152838116604483015260648201839052610fa99186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611034565b50505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516001600160a01b0383811660248301526044820183905261102f91859182169063a9059cbb90606401610f77565b505050565b5f6110486001600160a01b03841683611095565b905080515f1415801561106c57508080602001905181019061106a91906112f2565b155b1561102f57604051635274afe760e01b81526001600160a01b038416600482015260240161067e565b60606110a283835f6110a9565b9392505050565b6060814710156110ce5760405163cd78605960e01b815230600482015260240161067e565b5f80856001600160a01b031684866040516110e99190611311565b5f6040518083038185875af1925050503d805f8114611123576040519150601f19603f3d011682016040523d82523d5f602084013e611128565b606091505b5091509150611138868383611142565b9695505050505050565b606082611157576111528261119e565b6110a2565b815115801561116e57506001600160a01b0384163b155b1561119757604051639996b31560e01b81526001600160a01b038516600482015260240161067e565b50806110a2565b8051156111ae5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b03811681146111dd575f80fd5b919050565b5f602082840312156111f2575f80fd5b6110a2826111c7565b5f806040838503121561120c575f80fd5b611215836111c7565b946020939093013593505050565b5f8060408385031215611234575f80fd5b61123d836111c7565b915061124b602084016111c7565b90509250929050565b5f60208284031215611264575f80fd5b5035919050565b634e487b7160e01b5f52601160045260245ffd5b818103818111156104795761047961126b565b80820281158282048414176104795761047961126b565b5f826112c357634e487b7160e01b5f52601260045260245ffd5b500490565b808201808211156104795761047961126b565b5f602082840312156112eb575f80fd5b5051919050565b5f60208284031215611302575f80fd5b815180151581146110a2575f80fd5b5f82515f5b818110156113305760208186018101518583015201611316565b505f92019182525091905056fea2646970667358221220d10acc725b37650a1e3150e9ce340cf72305d8143757c5287cd11551748f11f964736f6c6343000815003300000000000000000000000023fd89fda78ec3cc436351599a7b12d48fe8fbce000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f3490000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000401ea2886d8c9e75ea5a47684d3f96082674fe4f0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da900000000000000000000000000000000000000000000000000000000003ca500

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610111575f3560e01c80638456cb591161009e578063e30c39781161006e578063e30c397814610253578063e5f9583114610264578063f2888dbb14610277578063f2fde38b1461028a578063f7c618c11461029d575f80fd5b80638456cb59146102005780638da5cb5b14610208578063a694fc3a14610219578063b51a4f241461022c575f80fd5b80635ebaf1db116100e45780635ebaf1db1461018b578063715018a6146101b25780637158da7c146101ba57806379ba5097146101e157806383cf7bcd146101e9575f80fd5b806319aeb94b146101155780633f28deca146101595780633f4ba83a1461016c5780635c975abb14610176575b5f80fd5b61013c7f000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f34981565b6040516001600160a01b0390911681526020015b60405180910390f35b61013c6101673660046110cf565b6102c4565b610174610356565b005b5f5460ff166040519015158152602001610150565b61013c7f0000000000000000000000004412e5492c689cf13d585dcdb010b3b8b12df16a81565b610174610370565b61013c7f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca081565b610174610389565b6101f260045481565b604051908152602001610150565b6101746103d2565b6002546001600160a01b031661013c565b6101746102273660046110ea565b6103ea565b61013c7f0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da981565b6003546001600160a01b031661013c565b6101746102723660046110ea565b610721565b6101746102853660046110cf565b61077e565b6101746102983660046110cf565b610afb565b61013c7f000000000000000000000000401ea2886d8c9e75ea5a47684d3f96082674fe4f81565b604051631f946f6560e11b81526001600160a01b0382811660048301525f917f000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f34990911690633f28deca90602401602060405180830381865afa15801561032c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103509190611101565b92915050565b61035e610b6c565b610366610b99565b61036e610bbb565b565b60405163830f4dd360e01b815260040160405180910390fd5b60035433906001600160a01b031681146103c65760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b6103cf81610c0c565b50565b6103da610b6c565b6103e2610c25565b61036e610c48565b6103f2610c84565b6103fa610c25565b80805f0361041b5760405163162908e360e11b815260040160405180910390fd5b604051631f946f6560e11b81523360048201525f907f000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f3496001600160a01b031690633f28deca90602401602060405180830381865afa15801561047f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a39190611101565b90506001600160a01b03811661053d57604051630d62fb6160e41b81523360048201527f000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f3496001600160a01b03169063d62fb610906024016020604051808303815f875af1158015610516573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061053a9190611101565b90505b604051839033907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d905f90a361059e6001600160a01b037f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca016333086610cae565b6105f26001600160a01b037f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0167f0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da985610d1b565b6001600160a01b037f0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da916637ca5643d82855f60405190808252806020026020018201604052801561064d578160200160208202803683370190505b506040518463ffffffff1660e01b815260040161066c9392919061111c565b5f604051808303815f87803b158015610683575f80fd5b505af1158015610695573d5f803e3d5ffd5b50506040516311f9fbc960e21b81526001600160a01b038481166004830152602482018790527f0000000000000000000000004412e5492c689cf13d585dcdb010b3b8b12df16a1692506347e7ef2491506044015f604051808303815f87803b158015610700575f80fd5b505af1158015610712573d5f803e3d5ffd5b5050505050506103cf60018055565b610729610b6c565b806004540361074b5760405163c23f6ccb60e01b815260040160405180910390fd5b6004546040518291907f5e756d56d50f99a044c06534300c7051941218b28582bc78c3f4e833b97d3163905f90a3600455565b610786610c84565b61078e610c25565b806001600160a01b0381166107b65760405163e6c4247b60e01b815260040160405180910390fd5b4260045411156107d957604051633d67494760e01b815260040160405180910390fd5b604051631f946f6560e11b81523360048201525f907f000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f3496001600160a01b031690633f28deca90602401602060405180830381865afa15801561083d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108619190611101565b6040516370a0823160e01b81526001600160a01b0380831660048301529192505f917f0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da916906370a0823190602401602060405180830381865afa1580156108ca573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108ee9190611178565b9050805f036109125760405163123ae41360e21b81523360048201526024016103bd565b6040516370a0823160e01b81526001600160a01b0383811660048301527f0000000000000000000000004412e5492c689cf13d585dcdb010b3b8b12df16a16906370a0823190602401602060405180830381865afa158015610976573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061099a9190611178565b60405133907f0f5bb82176feb1b5e747e28471aa92156a04d9f3ab9f45f28e2d704232b93f75905f90a36040516333a45dbb60e11b81526001600160a01b0383811660048301527f0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da9811660248301528581166044830152606482018390527f000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f3491690636748bb76906084015f604051808303815f87803b158015610a5b575f80fd5b505af1158015610a6d573d5f803e3d5ffd5b5050604051635312a01d60e11b81526001600160a01b03858116600483015287811660248301527f0000000000000000000000004412e5492c689cf13d585dcdb010b3b8b12df16a16925063a625403a91506044015f604051808303815f87803b158015610ad9575f80fd5b505af1158015610aeb573d5f803e3d5ffd5b505050505050506103cf60018055565b610b03610b6c565b600380546001600160a01b0383166001600160a01b03199091168117909155610b346002546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6002546001600160a01b0316331461036e5760405163118cdaa760e01b81523360048201526024016103bd565b5f5460ff1661036e57604051638dfc202b60e01b815260040160405180910390fd5b610bc3610b99565b5f805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600380546001600160a01b03191690556103cf81610da2565b5f5460ff161561036e5760405163d93c066560e01b815260040160405180910390fd5b610c50610c25565b5f805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258610bef3390565b600260015403610ca757604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b6040516001600160a01b038481166024830152838116604483015260648201839052610d159186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050610df3565b50505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015610d68573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8c9190611178565b9050610d158484610d9d858561118f565b610e59565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f610e076001600160a01b03841683610ee8565b905080515f14158015610e2b575080806020019051810190610e2991906111ae565b155b15610e5457604051635274afe760e01b81526001600160a01b03841660048201526024016103bd565b505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610eaa8482610efc565b610d15576040516001600160a01b0384811660248301525f6044830152610ede91869182169063095ea7b390606401610ce3565b610d158482610df3565b6060610ef583835f610f9d565b9392505050565b5f805f846001600160a01b031684604051610f1791906111cd565b5f604051808303815f865af19150503d805f8114610f50576040519150601f19603f3d011682016040523d82523d5f602084013e610f55565b606091505b5091509150818015610f7f575080511580610f7f575080806020019051810190610f7f91906111ae565b8015610f9457505f856001600160a01b03163b115b95945050505050565b606081471015610fc25760405163cd78605960e01b81523060048201526024016103bd565b5f80856001600160a01b03168486604051610fdd91906111cd565b5f6040518083038185875af1925050503d805f8114611017576040519150601f19603f3d011682016040523d82523d5f602084013e61101c565b606091505b509150915061102c868383611036565b9695505050505050565b60608261104b5761104682611092565b610ef5565b815115801561106257506001600160a01b0384163b155b1561108b57604051639996b31560e01b81526001600160a01b03851660048201526024016103bd565b5080610ef5565b8051156110a25780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b03811681146103cf575f80fd5b5f602082840312156110df575f80fd5b8135610ef5816110bb565b5f602082840312156110fa575f80fd5b5035919050565b5f60208284031215611111575f80fd5b8151610ef5816110bb565b5f606082016001600160a01b0386168352602085818501526060604085015281855180845260808601915082870193505f5b8181101561116a5784518352938301939183019160010161114e565b509098975050505050505050565b5f60208284031215611188575f80fd5b5051919050565b8082018082111561035057634e487b7160e01b5f52601160045260245ffd5b5f602082840312156111be575f80fd5b81518015158114610ef5575f80fd5b5f82515f5b818110156111ec57602081860181015185830152016111d2565b505f92019182525091905056fea26469706673582212201b9e236fba4f67525e5076c8a43b1cb43d970752fef9f77b7ca4240df2bce4ee64736f6c63430008150033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000023fd89fda78ec3cc436351599a7b12d48fe8fbce000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f3490000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000401ea2886d8c9e75ea5a47684d3f96082674fe4f0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da900000000000000000000000000000000000000000000000000000000003ca500

-----Decoded View---------------
Arg [0] : _initialOwner (address): 0x23fd89fDA78Ec3Cc436351599A7b12D48fE8FBcE
Arg [1] : _holdingManager (address): 0xAeB2204ac5042A6Bd68d4f6498B263bbfa03F349
Arg [2] : _underlyingAsset (address): 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
Arg [3] : _rewardToken (address): 0x401Ea2886D8C9e75EA5a47684d3F96082674fe4f
Arg [4] : _ionPool (address): 0x0000000000E33e35EE6052fae87bfcFac61b1da9
Arg [5] : _rewardsDuration (uint256): 3974400

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000023fd89fda78ec3cc436351599a7b12d48fe8fbce
Arg [1] : 000000000000000000000000aeb2204ac5042a6bd68d4f6498b263bbfa03f349
Arg [2] : 0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0
Arg [3] : 000000000000000000000000401ea2886d8c9e75ea5a47684d3f96082674fe4f
Arg [4] : 0000000000000000000000000000000000e33e35ee6052fae87bfcfac61b1da9
Arg [5] : 00000000000000000000000000000000000000000000000000000000003ca500


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.