ETH Price: $2,148.36 (-0.13%)
Gas: 0.04 Gwei

Contract

0x0000000b6bcCbd238329A55F83582EFd3b5D2Ed2
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim Investment246998812026-03-20 16:22:599 hrs ago1774023779IN
0x0000000b...d3b5D2Ed2
0 ETH0.000954492.18620732
Claim Investment245458772026-02-27 4:28:3521 days ago1772166515IN
0x0000000b...d3b5D2Ed2
0 ETH0.000022370.05462358
Claim Investment245458742026-02-27 4:27:5921 days ago1772166479IN
0x0000000b...d3b5D2Ed2
0 ETH0.000004760.05171336
Claim Investment245458712026-02-27 4:27:2321 days ago1772166443IN
0x0000000b...d3b5D2Ed2
0 ETH0.000026190.06089448
Claim Investment243884762026-02-05 5:14:1143 days ago1770268451IN
0x0000000b...d3b5D2Ed2
0 ETH0.000887052.13914636
Invest243064372026-01-24 18:26:4755 days ago1769279207IN
0x0000000b...d3b5D2Ed2
0 ETH0.001157672.10625025
Claim Investment242063852026-01-10 19:27:3569 days ago1768073255IN
0x0000000b...d3b5D2Ed2
0 ETH0.000258180.53031896
Claim Investment241716262026-01-05 23:04:3574 days ago1767654275IN
0x0000000b...d3b5D2Ed2
0 ETH0.000006010.05909328
Claim Investment241716222026-01-05 23:03:4774 days ago1767654227IN
0x0000000b...d3b5D2Ed2
0 ETH0.000005920.05816489
Claim Investment241716192026-01-05 23:03:1174 days ago1767654191IN
0x0000000b...d3b5D2Ed2
0 ETH0.000006340.06233203
Claim Investment241716162026-01-05 23:02:3574 days ago1767654155IN
0x0000000b...d3b5D2Ed2
0 ETH0.000035470.06177949
Claim Investment241688472026-01-05 13:45:4774 days ago1767620747IN
0x0000000b...d3b5D2Ed2
0 ETH0.000055130.14065057
Claim Investment240352652025-12-17 22:18:3593 days ago1766009915IN
0x0000000b...d3b5D2Ed2
0 ETH0.000250751.03151159
Claim Investment240352632025-12-17 22:18:1193 days ago1766009891IN
0x0000000b...d3b5D2Ed2
0 ETH0.000328491.02848027
Claim Investment240352572025-12-17 22:16:5993 days ago1766009819IN
0x0000000b...d3b5D2Ed2
0 ETH0.000070240.11062612
Claim Investment240352502025-12-17 22:15:3593 days ago1766009735IN
0x0000000b...d3b5D2Ed2
0 ETH0.000023260.22825614
Claim Investment240352402025-12-17 22:13:3593 days ago1766009615IN
0x0000000b...d3b5D2Ed2
0 ETH0.000151720.2339152
Claim Investment240269972025-12-16 18:35:4794 days ago1765910147IN
0x0000000b...d3b5D2Ed2
0 ETH0.001284952.03489767
Claim Investment240167942025-12-15 8:25:2395 days ago1765787123IN
0x0000000b...d3b5D2Ed2
0 ETH0.000089530.368302
Claim Investment239982092025-12-12 18:04:5998 days ago1765562699IN
0x0000000b...d3b5D2Ed2
0 ETH0.000058830.22962271
Claim Investment239982072025-12-12 18:04:3598 days ago1765562675IN
0x0000000b...d3b5D2Ed2
0 ETH0.000135920.53050379
Claim Investment239982042025-12-12 18:03:5998 days ago1765562639IN
0x0000000b...d3b5D2Ed2
0 ETH0.000135370.52832692
Claim Investment239981992025-12-12 18:02:5998 days ago1765562579IN
0x0000000b...d3b5D2Ed2
0 ETH0.000265771.03721029
Claim Rewards239981692025-12-12 17:56:5998 days ago1765562219IN
0x0000000b...d3b5D2Ed2
0 ETH0.000243771.02758412
Claim Investment238847232025-11-26 18:33:11114 days ago1764181991IN
0x0000000b...d3b5D2Ed2
0 ETH0.000562351.2322102
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x60a06040222956552025-04-18 11:42:47336 days ago1744976567  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
StrategyManager

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 100000 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SignedMath } from "@openzeppelin/contracts/utils/math/SignedMath.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import { OperationsLib } from "./libraries/OperationsLib.sol";

import { IHolding } from "./interfaces/core/IHolding.sol";
import { IHoldingManager } from "./interfaces/core/IHoldingManager.sol";
import { IManager } from "./interfaces/core/IManager.sol";

import { ISharesRegistry } from "./interfaces/core/ISharesRegistry.sol";
import { IStablesManager } from "./interfaces/core/IStablesManager.sol";
import { IStrategy } from "./interfaces/core/IStrategy.sol";
import { IStrategyManager } from "./interfaces/core/IStrategyManager.sol";

/**
 * @title StrategyManager
 *
 * @notice Manages investments of the user's assets into the whitelisted strategies to generate applicable revenue.
 *
 * @dev This contract inherits functionalities from  `Ownable2Step`, `ReentrancyGuard`, and `Pausable`.
 *
 * @author Hovooo (@hovooo), Cosmin Grigore (@gcosmintech).
 *
 * @custom:security-contact support@jigsaw.finance
 */
contract StrategyManager is IStrategyManager, Ownable2Step, ReentrancyGuard, Pausable {
    using EnumerableSet for EnumerableSet.AddressSet;
    using SafeERC20 for IERC20;
    using SignedMath for int256;

    /**
     * @notice Returns whitelisted Strategies' info.
     */
    mapping(address strategy => StrategyInfo info) public override strategyInfo;

    /**
     * @notice Stores the strategies holding has invested in.
     */
    mapping(address holding => EnumerableSet.AddressSet strategies) private holdingToStrategy;

    /**
     * @notice Contract that contains all the necessary configs of the protocol.
     */
    IManager public immutable override manager;

    /**
     * @notice Creates a new StrategyManager contract.
     * @param _initialOwner The initial owner of the contract.
     * @param _manager Contract that holds all the necessary configs of the protocol.
     */
    constructor(address _initialOwner, address _manager) Ownable(_initialOwner) {
        require(_manager != address(0), "3065");
        manager = IManager(_manager);
    }

    // -- User specific methods --

    /**
     * @notice Invests `_token` into `_strategy`.
     *
     * @notice Requirements:
     * - Strategy must be whitelisted.
     * - Amount must be non-zero.
     * - Token specified for investment must be whitelisted.
     * - Msg.sender must have holding.
     *
     * @notice Effects:
     * - Performs investment to the specified `_strategy`.
     * - Deposits holding's collateral to the specified `_strategy`.
     * - Adds `_strategy` used for investment to the holdingToStrategy data structure.
     *
     * @notice Emits:
     * - Invested event indicating successful investment operation.
     *
     * @param _token address.
     * @param _strategy address.
     * @param _amount to be invested.
     * @param _minSharesAmountOut minimum amount of shares to receive.
     * @param _data needed by each individual strategy.
     *
     * @return tokenOutAmount receipt tokens amount.
     * @return tokenInAmount tokenIn amount.
     */
    function invest(
        address _token,
        address _strategy,
        uint256 _amount,
        uint256 _minSharesAmountOut,
        bytes calldata _data
    )
        external
        override
        validStrategy(_strategy)
        validAmount(_amount)
        validToken(_token)
        whenNotPaused
        nonReentrant
        returns (uint256 tokenOutAmount, uint256 tokenInAmount)
    {
        address _holding = _getHoldingManager().userHolding(msg.sender);
        require(_getHoldingManager().isHolding(_holding), "3002");
        require(strategyInfo[_strategy].active, "1202");
        require(IStrategy(_strategy).tokenIn() == _token, "3085");

        (tokenOutAmount, tokenInAmount) = _invest({
            _holding: _holding,
            _token: _token,
            _strategy: _strategy,
            _amount: _amount,
            _minSharesAmountOut: _minSharesAmountOut,
            _data: _data
        });

        emit Invested(_holding, msg.sender, _token, _strategy, _amount, tokenOutAmount, tokenInAmount);
        return (tokenOutAmount, tokenInAmount);
    }

    /**
     * @notice Claims investment from one strategy and invests it into another.
     *
     * @notice Requirements:
     * - The `strategyTo` must be valid and active.
     * - The `strategyFrom` and `strategyTo` must be different.
     * - Msg.sender must have a holding.
     *
     * @notice Effects:
     * - Claims the investment from `strategyFrom`.
     * - Invests the claimed amount into `strategyTo`.
     *
     * @notice Emits:
     * - InvestmentMoved event indicating successful investment movement operation.
     *
     * @dev Some strategies won't give back any receipt tokens; in this case 'tokenOutAmount' will be 0.
     * @dev 'tokenInAmount' will be equal to '_amount' in case the '_asset' is the same as strategy 'tokenIn()'.
     *
     * @param _token The address of the token.
     * @param _data The MoveInvestmentData object containing strategy and amount details.
     *
     * @return tokenOutAmount The amount of receipt tokens returned.
     * @return tokenInAmount The amount of tokens invested in the new strategy.
     */
    function moveInvestment(
        address _token,
        MoveInvestmentData calldata _data
    )
        external
        override
        validStrategy(_data.strategyFrom)
        validStrategy(_data.strategyTo)
        nonReentrant
        whenNotPaused
        returns (uint256 tokenOutAmount, uint256 tokenInAmount)
    {
        address _holding = _getHoldingManager().userHolding(msg.sender);
        require(_getHoldingManager().isHolding(_holding), "3002");
        require(_data.strategyFrom != _data.strategyTo, "3086");
        require(strategyInfo[_data.strategyTo].active, "1202");
        require(IStrategy(_data.strategyFrom).tokenIn() == _token, "3001");
        require(IStrategy(_data.strategyTo).tokenIn() == _token, "3085");

        (uint256 claimResult,,,) = _claimInvestment({
            _holding: _holding,
            _token: _token,
            _strategy: _data.strategyFrom,
            _shares: _data.shares,
            _data: _data.dataFrom
        });
        (tokenOutAmount, tokenInAmount) = _invest({
            _holding: _holding,
            _token: _token,
            _strategy: _data.strategyTo,
            _amount: claimResult,
            _minSharesAmountOut: _data.strategyToMinSharesAmountOut,
            _data: _data.dataTo
        });

        emit InvestmentMoved(
            _holding,
            msg.sender,
            _token,
            _data.strategyFrom,
            _data.strategyTo,
            _data.shares,
            tokenOutAmount,
            tokenInAmount
        );

        return (tokenOutAmount, tokenInAmount);
    }

    /**
     * @notice Claims a strategy investment.
     *
     * @notice Requirements:
     * - The `_strategy` must be valid.
     * - Msg.sender must be allowed to execute the call.
     * - `_shares` must be of valid amount.
     * - Specified `_holding` must exist within protocol.
     *
     * @notice Effects:
     * - Withdraws investment from `_strategy`.
     * - Updates `holdingToStrategy` if needed.
     *
     * @notice Emits:
     * - StrategyClaim event indicating successful claim operation.
     *
     * @dev Withdraws investment from a strategy.
     * @dev Some strategies will allow only the tokenIn to be withdrawn.
     * @dev 'AssetAmount' will be equal to 'tokenInAmount' in case the '_asset' is the same as strategy 'tokenIn()'.
     *
     * @param _holding holding's address.
     * @param _token address to be received.
     * @param _strategy strategy to invest into.
     * @param _shares shares amount.
     * @param _data extra data.
     *
     * @return withdrawnAmount returned asset amount obtained from the operation.
     * @return initialInvestment returned token in amount.
     * @return yield The yield amount (positive for profit, negative for loss)
     * @return fee The amount of fee charged by the strategy
     */
    function claimInvestment(
        address _holding,
        address _token,
        address _strategy,
        uint256 _shares,
        bytes calldata _data
    )
        external
        override
        validStrategy(_strategy)
        onlyAllowed(_holding)
        validAmount(_shares)
        nonReentrant
        whenNotPaused
        returns (uint256 withdrawnAmount, uint256 initialInvestment, int256 yield, uint256 fee)
    {
        require(_getHoldingManager().isHolding(_holding), "3002");
        (withdrawnAmount, initialInvestment, yield, fee) = _claimInvestment({
            _holding: _holding,
            _token: _token,
            _strategy: _strategy,
            _shares: _shares,
            _data: _data
        });

        emit StrategyClaim({
            holding: _holding,
            user: msg.sender,
            token: _token,
            strategy: _strategy,
            shares: _shares,
            withdrawnAmount: withdrawnAmount,
            initialInvestment: initialInvestment,
            yield: yield,
            fee: fee
        });
    }

    /**
     * @notice Claims rewards from strategy.
     *
     * @notice Requirements:
     * - The `_strategy` must be valid.
     * - Msg.sender must have valid holding within protocol.
     *
     * @notice Effects:
     * - Claims rewards from strategies.
     * - Adds accrued rewards as a collateral for holding.
     *
     * @param _strategy strategy to invest into.
     * @param _data extra data.
     *
     * @return rewards reward amounts.
     * @return tokens reward tokens.
     */
    function claimRewards(
        address _strategy,
        bytes calldata _data
    )
        external
        override
        validStrategy(_strategy)
        nonReentrant
        whenNotPaused
        returns (uint256[] memory rewards, address[] memory tokens)
    {
        address _holding = _getHoldingManager().userHolding(msg.sender);
        require(_getHoldingManager().isHolding(_holding), "3002");

        (rewards, tokens) = IStrategy(_strategy).claimRewards({ _recipient: _holding, _data: _data });

        for (uint256 i = 0; i < rewards.length; i++) {
            _accrueRewards({ _token: tokens[i], _amount: rewards[i], _holding: _holding });
        }
    }

    // -- Administration --

    /**
     * @notice Adds a new strategy to the whitelist.
     * @param _strategy strategy's address.
     */
    function addStrategy(
        address _strategy
    ) public override onlyOwner validAddress(_strategy) {
        require(!strategyInfo[_strategy].whitelisted, "3014");
        StrategyInfo memory info = StrategyInfo(0, false, false);
        info.performanceFee = manager.performanceFee();
        info.active = true;
        info.whitelisted = true;

        strategyInfo[_strategy] = info;

        emit StrategyAdded(_strategy);
    }

    /**
     * @notice Updates an existing strategy info.
     * @param _strategy strategy's address.
     * @param _info info.
     */
    function updateStrategy(
        address _strategy,
        StrategyInfo calldata _info
    ) external override onlyOwner validStrategy(_strategy) {
        require(_info.whitelisted, "3104");
        require(_info.performanceFee <= OperationsLib.FEE_FACTOR, "3105");
        strategyInfo[_strategy] = _info;
        emit StrategyUpdated(_strategy, _info.active, _info.performanceFee);
    }

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

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

    /**
     * @notice Override to avoid losing contract ownership.
     */
    function renounceOwnership() public pure override {
        revert("1000");
    }

    // -- Getters --

    /**
     * @notice Returns all the strategies holding has invested in.
     * @dev Should be only called off-chain as can be high gas consuming.
     * @param _holding address for which the strategies are requested.
     */
    function getHoldingToStrategy(
        address _holding
    ) external view returns (address[] memory) {
        return holdingToStrategy[_holding].values();
    }

    /**
     * @notice Returns the number of strategies the holding has invested in.
     * @param _holding address for which the strategy count is requested.
     * @return uint256 The number of strategies the holding has invested in.
     */
    function getHoldingToStrategyLength(
        address _holding
    ) external view returns (uint256) {
        return holdingToStrategy[_holding].length();
    }

    // -- Private methods --

    /**
     * @notice Accrues rewards for a specific token and amount to a holding address.
     *
     * @notice Effects:
     * - Adds collateral to the holding if the amount is greater than 0 and the share registry address is not zero.
     *
     * @notice Emits:
     * - CollateralAdjusted event indicating successful collateral adjustment operation.
     *
     * @param _token address for which rewards are being accrued.
     * @param _amount of the token to accrue as rewards.
     * @param _holding address to which the rewards are accrued.
     */
    function _accrueRewards(address _token, uint256 _amount, address _holding) private {
        if (_amount > 0) {
            (bool active, address shareRegistry) = _getStablesManager().shareRegistryInfo(_token);

            if (shareRegistry != address(0) && active) {
                //add collateral
                emit CollateralAdjusted(_holding, _token, _amount, true);
                _getStablesManager().addCollateral(_holding, _token, _amount);
            }
        }
    }

    /**
     * @notice Invests a specified amount of a token from a holding into a strategy.
     *
     * @notice Effects:
     * - Deposits the specified amount of the token into the given strategy.
     * - Updates the holding's invested strategies set.
     *
     * @param _holding address from which the investment is made.
     * @param _token address to be invested.
     * @param _strategy address into which the token is invested.
     * @param _amount token to invest.
     * @param _minSharesAmountOut minimum amount of shares to receive.
     * @param _data required by the strategy's deposit function.
     *
     * @return tokenOutAmount The amount of tokens received from the strategy.
     * @return tokenInAmount The amount of tokens invested into the strategy.
     */
    function _invest(
        address _holding,
        address _token,
        address _strategy,
        uint256 _amount,
        uint256 _minSharesAmountOut,
        bytes calldata _data
    ) private returns (uint256 tokenOutAmount, uint256 tokenInAmount) {
        (tokenOutAmount, tokenInAmount) = IStrategy(_strategy).deposit(_token, _amount, _holding, _data);
        require(tokenOutAmount != 0 && tokenOutAmount >= _minSharesAmountOut, "3030");

        // Ensure holding is not liquidatable after investment
        require(!_getStablesManager().isLiquidatable(_token, _holding), "3103");

        // Add strategy to the set, which stores holding's all invested strategies
        holdingToStrategy[_holding].add(_strategy);
    }

    /**
     * @notice Withdraws invested amount from a strategy.
     *
     * @notice Effects:
     * - Withdraws investment from `_strategy`.
     * - Removes strategy from holding's invested strategies set if `remainingShares` == 0.
     *
     * @param _holding address from which the investment is being claimed.
     * @param _token address to be withdrawn from the strategy.
     * @param _strategy address from which the investment is being claimed.
     * @param _shares number to be withdrawn from the strategy.
     * @param _data data required by the strategy's withdraw function.
     *
     * @return assetResult The amount of the asset withdrawn from the strategy.
     * @return tokenInResult The amount of tokens received in exchange for the withdrawn asset.
     */
    function _claimInvestment(
        address _holding,
        address _token,
        address _strategy,
        uint256 _shares,
        bytes calldata _data
    ) private returns (uint256, uint256, int256, uint256) {
        ClaimInvestmentData memory tempData = ClaimInvestmentData({
            strategyContract: IStrategy(_strategy),
            withdrawnAmount: 0,
            initialInvestment: 0,
            yield: 0,
            fee: 0,
            remainingShares: 0
        });

        // First check if holding has enough receipt tokens to burn.
        _checkReceiptTokenAvailability({ _strategy: tempData.strategyContract, _shares: _shares, _holding: _holding });

        (tempData.withdrawnAmount, tempData.initialInvestment, tempData.yield, tempData.fee) =
            tempData.strategyContract.withdraw({ _shares: _shares, _recipient: _holding, _asset: _token, _data: _data });
        require(tempData.withdrawnAmount > 0, "3016");

        if (tempData.yield > 0) {
            _getStablesManager().addCollateral({ _holding: _holding, _token: _token, _amount: uint256(tempData.yield) });
        }
        if (tempData.yield < 0) {
            _getStablesManager().removeCollateral({ _holding: _holding, _token: _token, _amount: tempData.yield.abs() });
        }

        // Ensure user doesn't harm themselves by becoming liquidatable after claiming investment.
        // If function is called by liquidation manager, we don't need to check if holding is liquidatable,
        // as we need to save as much collateral as possible.
        if (manager.liquidationManager() != msg.sender) {
            require(!_getStablesManager().isLiquidatable(_token, _holding), "3103");
        }

        // If after the claim holding no longer has shares in the strategy remove that strategy from the set.
        (, tempData.remainingShares) = tempData.strategyContract.recipients(_holding);
        if (0 == tempData.remainingShares) holdingToStrategy[_holding].remove(_strategy);

        return (tempData.withdrawnAmount, tempData.initialInvestment, tempData.yield, tempData.fee);
    }

    /**
     * @notice Checks the availability of receipt tokens in the holding.
     *
     * @notice Requirements:
     * - Holding must have enough receipt tokens for the specified number of shares.
     *
     * @param _strategy contract's instance.
     * @param _shares number being checked for receipt token availability.
     * @param _holding address for which the receipt token availability is being checked.
     */
    function _checkReceiptTokenAvailability(IStrategy _strategy, uint256 _shares, address _holding) private view {
        uint256 tokenDecimals = _strategy.sharesDecimals();
        (, uint256 totalShares) = _strategy.recipients(_holding);
        uint256 rtAmount = _shares > totalShares ? totalShares : _shares;

        if (tokenDecimals > 18) {
            rtAmount = rtAmount / (10 ** (tokenDecimals - 18));
        } else {
            rtAmount = rtAmount * (10 ** (18 - tokenDecimals));
        }

        require(IERC20(_strategy.getReceiptTokenAddress()).balanceOf(_holding) >= rtAmount);
    }

    /**
     * @notice Retrieves the instance of the Holding Manager contract.
     * @return IHoldingManager contract's instance.
     */
    function _getHoldingManager() private view returns (IHoldingManager) {
        return IHoldingManager(manager.holdingManager());
    }

    /**
     * @notice Retrieves the instance of the Stables Manager contract.
     * @return IStablesManager contract's instance.
     */
    function _getStablesManager() private view returns (IStablesManager) {
        return IStablesManager(manager.stablesManager());
    }

    // -- Modifiers --

    /**
     * @dev Modifier to check if the address is valid (not zero address).
     * @param _address being checked.
     */
    modifier validAddress(
        address _address
    ) {
        require(_address != address(0), "3000");
        _;
    }

    /**
     * @dev Modifier to check if the strategy address is valid (whitelisted).
     * @param _strategy address being checked.
     */
    modifier validStrategy(
        address _strategy
    ) {
        require(strategyInfo[_strategy].whitelisted, "3029");
        _;
    }

    /**
     * @dev Modifier to check if the amount is valid (greater than zero).
     * @param _amount being checked.
     */
    modifier validAmount(
        uint256 _amount
    ) {
        require(_amount > 0, "2001");
        _;
    }

    /**
     * @dev Modifier to check if the sender is allowed to perform the action.
     * @param _holding address being accessed.
     */
    modifier onlyAllowed(
        address _holding
    ) {
        require(
            manager.liquidationManager() == msg.sender || _getHoldingManager().holdingUser(_holding) == msg.sender,
            "1000"
        );
        _;
    }

    /**
     * @dev Modifier to check if the token is valid (whitelisted).
     * @param _token address being checked.
     */
    modifier validToken(
        address _token
    ) {
        require(manager.isTokenWhitelisted(_token), "3001");
        _;
    }
}

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// 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;
    }
}

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

// 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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title Operations Library
 * @notice A library containing common mathematical operations used throughout the protocol
 */
library OperationsLib {
    /**
     * @notice The denominator used for fee calculations (10,000 = 100%)
     * @dev Fees are expressed in basis points, where 1 basis point = 0.01%
     *      For example, 100 = 1%, 500 = 5%, 1000 = 10%
     */
    uint256 internal constant FEE_FACTOR = 10_000;

    /**
     * @notice Calculates the absolute fee amount based on the input amount and fee rate
     * @dev The calculation rounds up to ensure the protocol always collects the full fee
     * @param amount The base amount on which the fee is calculated
     * @param fee The fee rate in basis points (e.g., 100 = 1%)
     * @return The absolute fee amount, rounded up if there's any remainder
     */
    function getFeeAbsolute(uint256 amount, uint256 fee) internal pure returns (uint256) {
        // Calculate fee amount with rounding up to avoid precision loss
        return (amount * fee) / FEE_FACTOR + (amount * fee % FEE_FACTOR == 0 ? 0 : 1);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

/**
 * @title IHolding
 * @dev Interface for the Holding Contract.
 */
interface IHolding {
    // -- Events --

    /**
     * @notice Emitted when the emergency invoker is set.
     */
    event EmergencyInvokerSet(address indexed oldInvoker, address indexed newInvoker);

    // -- State variables --

    /**
     * @notice Returns the emergency invoker address.
     * @return The address of the emergency invoker.
     */
    function emergencyInvoker() external view returns (address);

    /**
     * @notice Contract that contains all the necessary configs of the protocol.
     */
    function manager() external view returns (IManager);

    // -- User specific methods --

    /**
     * @notice Sets the emergency invoker address for this holding.
     *
     * @notice Requirements:
     * - The caller must be the owner of this holding.
     *
     * @notice Effects:
     * - Updates the emergency invoker address to the provided value.
     * - Emits an event to track the change for off-chain monitoring.
     *
     * @param _emergencyInvoker The address to set as the emergency invoker.
     */
    function setEmergencyInvoker(
        address _emergencyInvoker
    ) external;

    /**
     * @notice Approves an `_amount` of a specified token to be spent on behalf of the `msg.sender` by `_destination`.
     *
     * @notice Requirements:
     * - The caller must be allowed to make this call.
     *
     * @notice Effects:
     * - Safe approves the `_amount` of `_tokenAddress` to `_destination`.
     *
     * @param _tokenAddress Token user to be spent.
     * @param _destination Destination address of the approval.
     * @param _amount Withdrawal amount.
     */
    function approve(address _tokenAddress, address _destination, uint256 _amount) external;

    /**
     * @notice Transfers `_token` from the holding contract to `_to` address.
     *
     * @notice Requirements:
     * - The caller must be allowed.
     *
     * @notice Effects:
     * - Safe transfers `_amount` of `_token` to `_to`.
     *
     * @param _token Token address.
     * @param _to Address to move token to.
     * @param _amount Transfer amount.
     */
    function transfer(address _token, address _to, uint256 _amount) external;

    /**
     * @notice Executes generic call on the `contract`.
     *
     * @notice Requirements:
     * - The caller must be allowed.
     *
     * @notice Effects:
     * - Makes a low-level call to the `_contract` with the provided `_call` data.
     *
     * @param _contract The contract address for which the call will be invoked.
     * @param _call Abi.encodeWithSignature data for the call.
     *
     * @return success Indicates if the call was successful.
     * @return result The result returned by the call.
     */
    function genericCall(
        address _contract,
        bytes calldata _call
    ) external payable returns (bool success, bytes memory result);

    /**
     * @notice Executes an emergency generic call on the specified contract.
     *
     * @notice Requirements:
     * - The caller must be the designated emergency invoker.
     * - The emergency invoker must be an allowed invoker in the Manager contract.
     * - Protected by nonReentrant modifier to prevent reentrancy attacks.
     *
     * @notice Effects:
     * - Makes a low-level call to the `_contract` with the provided `_call` data.
     * - Forwards any ETH value sent with the transaction.
     *
     * @param _contract The contract address for which the call will be invoked.
     * @param _call Abi.encodeWithSignature data for the call.
     *
     * @return success Indicates if the call was successful.
     * @return result The result returned by the call.
     */
    function emergencyGenericCall(
        address _contract,
        bytes calldata _call
    ) external payable returns (bool success, bytes memory result);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

/**
 * @title IHoldingManager
 * @notice Interface for the Holding Manager.
 */
interface IHoldingManager {
    // -- Custom types --

    /**
     * @notice Data used for multiple borrow.
     */
    struct BorrowData {
        address token;
        uint256 amount;
        uint256 minJUsdAmountOut;
    }

    /**
     * @notice Data used for multiple repay.
     */
    struct RepayData {
        address token;
        uint256 amount;
    }

    // -- Events --

    /**
     * @notice Emitted when a new Holding is created.
     * @param user The address of the user.
     * @param holdingAddress The address of the created holding.
     */
    event HoldingCreated(address indexed user, address indexed holdingAddress);

    /**
     * @notice Emitted when a deposit is made.
     * @param holding The address of the holding.
     * @param token The address of the token.
     * @param amount The amount deposited.
     */
    event Deposit(address indexed holding, address indexed token, uint256 amount);

    /**
     * @notice Emitted when a borrow action is performed.
     * @param holding The address of the holding.
     * @param token The address of the token.
     * @param jUsdMinted The amount of jUSD minted.
     * @param mintToUser Indicates if the amount is minted directly to the user.
     */
    event Borrowed(address indexed holding, address indexed token, uint256 jUsdMinted, bool mintToUser);

    /**
     * @notice Emitted when a borrow event happens using multiple collateral types.
     * @param holding The address of the holding.
     * @param length The number of borrow operations.
     * @param mintedToUser Indicates if the amounts are minted directly to the users.
     */
    event BorrowedMultiple(address indexed holding, uint256 length, bool mintedToUser);

    /**
     * @notice Emitted when a repay action is performed.
     * @param holding The address of the holding.
     * @param token The address of the token.
     * @param amount The amount repaid.
     * @param repayFromUser Indicates if the repayment is from the user's wallet.
     */
    event Repaid(address indexed holding, address indexed token, uint256 amount, bool repayFromUser);

    /**
     * @notice Emitted when a multiple repay operation happens.
     * @param holding The address of the holding.
     * @param length The number of repay operations.
     * @param repaidFromUser Indicates if the repayments are from the users' wallets.
     */
    event RepaidMultiple(address indexed holding, uint256 length, bool repaidFromUser);

    /**
     * @notice Emitted when the user wraps native coin.
     * @param user The address of the user.
     * @param amount The amount wrapped.
     */
    event NativeCoinWrapped(address user, uint256 amount);

    /**
     * @notice Emitted when the user unwraps into native coin.
     * @param user The address of the user.
     * @param amount The amount unwrapped.
     */
    event NativeCoinUnwrapped(address user, uint256 amount);

    /**
     * @notice Emitted when tokens are withdrawn from the holding.
     * @param holding The address of the holding.
     * @param token The address of the token.
     * @param totalAmount The total amount withdrawn.
     * @param feeAmount The fee amount.
     */
    event Withdrawal(address indexed holding, address indexed token, uint256 totalAmount, uint256 feeAmount);

    /**
     * @notice Emitted when the contract receives ETH.
     * @param from The address of the sender.
     * @param amount The amount received.
     */
    event Received(address indexed from, uint256 amount);

    // -- State variables --

    /**
     * @notice Returns the holding for a user.
     * @param _user The address of the user.
     * @return The address of the holding.
     */
    function userHolding(
        address _user
    ) external view returns (address);

    /**
     * @notice Returns the user for a holding.
     * @param holding The address of the holding.
     * @return The address of the user.
     */
    function holdingUser(
        address holding
    ) external view returns (address);

    /**
     * @notice Returns true if the holding was created.
     * @param _holding The address of the holding.
     * @return True if the holding was created, false otherwise.
     */
    function isHolding(
        address _holding
    ) external view returns (bool);

    /**
     * @notice Returns the address of the holding implementation to be cloned from.
     * @return The address of the current holding implementation.
     */
    function holdingImplementationReference() external view returns (address);

    /**
     * @notice Contract that contains all the necessary configs of the protocol.
     * @return The manager contract.
     */
    function manager() external view returns (IManager);

    /**
     * @notice Returns the address of the WETH contract to save on `manager.WETH()` calls.
     * @return The address of the WETH contract.
     */
    function WETH() external view returns (address);

    // -- User specific methods --

    /**
     * @notice Creates holding for the msg.sender.
     *
     * @notice Requirements:
     * - `msg.sender` must not have a holding within the protocol, as only one holding is allowed per address.
     * - Must be called from an EOA or whitelisted contract.
     *
     * @notice Effects:
     * - Clones `holdingImplementationReference`.
     * - Updates `userHolding` and `holdingUser` mappings with newly deployed `newHoldingAddress`.
     * - Initiates the `newHolding`.
     *
     * @notice Emits:
     * - `HoldingCreated` event indicating successful Holding creation.
     *
     * @return The address of the new holding.
     */
    function createHolding() external returns (address);

    /**
     * @notice Deposits a whitelisted token into the Holding.
     *
     * @notice Requirements:
     * - `_token` must be a whitelisted token.
     * - `_amount` must be greater than zero.
     * - `msg.sender` must have a valid holding.
     *
     * @param _token Token's address.
     * @param _amount Amount to deposit.
     */
    function deposit(address _token, uint256 _amount) external;

    /**
     * @notice Wraps native coin and deposits WETH into the holding.
     *
     * @dev This function must receive ETH in the transaction.
     *
     * @notice Requirements:
     *  - WETH must be whitelisted within protocol.
     * - `msg.sender` must have a valid holding.
     */
    function wrapAndDeposit() external payable;

    /**
     * @notice Withdraws a token from a Holding to a user.
     *
     * @notice Requirements:
     * - `_token` must be a valid address.
     * - `_amount` must be greater than zero.
     * - `msg.sender` must have a valid holding.
     *
     * @notice Effects:
     * - Withdraws the `_amount` of `_token` from the holding.
     * - Transfers the `_amount` of `_token` to `msg.sender`.
     * - Deducts any applicable fees.
     *
     * @param _token Token user wants to withdraw.
     * @param _amount Withdrawal amount.
     */
    function withdraw(address _token, uint256 _amount) external;

    /**
     * @notice Withdraws WETH from holding and unwraps it before sending it to the user.
     *
     * @notice Requirements:
     * - `_amount` must be greater than zero.
     * - `msg.sender` must have a valid holding.
     * - The low level native coin transfers must succeed.
     *
     * @notice Effects
     * - Transfers WETH from Holding address to address(this).
     * - Unwraps the WETH into native coin.
     * - Withdraws the `_amount` of WETH from the holding.
     * - Deducts any applicable fees.
     * - Transfers the unwrapped amount to `msg.sender`.
     *
     * @param _amount Withdrawal amount.
     */
    function withdrawAndUnwrap(
        uint256 _amount
    ) external;

    /**
     * @notice Borrows jUSD stablecoin to the user or to the holding contract.
     *
     * @dev The _amount does not account for the collateralization ratio and is meant to represent collateral's amount
     * equivalent to jUSD's value the user wants to receive.
     * @dev Ensure that the user will not become insolvent after borrowing before calling this function, as this
     * function will revert ("3009") if the supplied `_amount` does not adhere to the collateralization ratio set in
     * the registry for the specific collateral.
     *
     * @notice Requirements:
     * - `msg.sender` must have a valid holding.
     *
     * @notice Effects:
     * - Calls borrow function on `Stables Manager` Contract resulting in minting stablecoin based on the `_amount` of
     * `_token` collateral.
     *
     * @notice Emits:
     * - `Borrowed` event indicating successful borrow operation.
     *
     * @param _token Collateral token.
     * @param _amount The collateral amount equivalent for borrowed jUSD.
     * @param _mintDirectlyToUser If true, mints to user instead of holding.
     * @param _minJUsdAmountOut The minimum amount of jUSD that is expected to be received.
     *
     * @return jUsdMinted The amount of jUSD minted.
     */
    function borrow(
        address _token,
        uint256 _amount,
        uint256 _minJUsdAmountOut,
        bool _mintDirectlyToUser
    ) external returns (uint256 jUsdMinted);

    /**
     * @notice Borrows jUSD stablecoin to the user or to the holding contract using multiple collaterals.
     *
     * @dev This function will fail if any `amount` supplied in the `_data` does not adhere to the collateralization
     * ratio set in the registry for the specific collateral. For instance, if the collateralization ratio is 200%, the
     * maximum `_amount` that can be used to borrow is half of the user's free collateral, otherwise the user's holding
     * will become insolvent after borrowing.
     *
     * @notice Requirements:
     * - `msg.sender` must have a valid holding.
     * - `_data` must contain at least one entry.
     *
     * @notice Effects:
     * - Mints jUSD stablecoin for each entry in `_data` based on the collateral amounts.
     *
     * @notice Emits:
     * - `Borrowed` event for each entry indicating successful borrow operation.
     * - `BorrowedMultiple` event indicating successful multiple borrow operation.
     *
     * @param _data Struct containing data for each collateral type.
     * @param _mintDirectlyToUser If true, mints to user instead of holding.
     *
     * @return  The amount of jUSD minted for each collateral type.
     */
    function borrowMultiple(
        BorrowData[] calldata _data,
        bool _mintDirectlyToUser
    ) external returns (uint256[] memory);

    /**
     * @notice Repays jUSD stablecoin debt from the user's or to the holding's address and frees up the locked
     * collateral.
     *
     * @notice Requirements:
     * - `msg.sender` must have a valid holding.
     *
     * @notice Effects:
     * - Repays `_amount` jUSD stablecoin.
     *
     * @notice Emits:
     * - `Repaid` event indicating successful debt repayment operation.
     *
     * @param _token Collateral token.
     * @param _amount The repaid amount.
     * @param _repayFromUser If true, Stables Manager will burn jUSD from the msg.sender, otherwise user's holding.
     */
    function repay(address _token, uint256 _amount, bool _repayFromUser) external;

    /**
     * @notice Repays multiple jUSD stablecoin debts from the user's or to the holding's address and frees up the locked
     * collateral assets.
     *
     * @notice Requirements:
     * - `msg.sender` must have a valid holding.
     * - `_data` must contain at least one entry.
     *
     * @notice Effects:
     * - Repays stablecoin for each entry in `_data.
     *
     * @notice Emits:
     * - `Repaid` event indicating successful debt repayment operation.
     * - `RepaidMultiple` event indicating successful multiple repayment operation.
     *
     * @param _data Struct containing data for each collateral type.
     * @param _repayFromUser If true, it will burn from user's wallet, otherwise from user's holding.
     */
    function repayMultiple(RepayData[] calldata _data, bool _repayFromUser) external;

    // -- Administration --

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

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

File 13 of 26 : IManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IOracle } from "../oracle/IOracle.sol";

/**
 * @title IManager.
 * @dev Interface for the Manager Contract.
 */
interface IManager {
    // -- Events --

    /**
     * @notice Emitted when a new contract is whitelisted.
     * @param contractAddress The address of the contract that is whitelisted.
     */
    event ContractWhitelisted(address indexed contractAddress);

    /**
     * @notice Emitted when a contract is removed from the whitelist.
     * @param contractAddress The address of the contract that is removed from the whitelist.
     */
    event ContractBlacklisted(address indexed contractAddress);

    /**
     * @notice Emitted when a new token is whitelisted.
     * @param token The address of the token that is whitelisted.
     */
    event TokenWhitelisted(address indexed token);

    /**
     * @notice Emitted when a new token is removed from the whitelist.
     * @param token The address of the token that is removed from the whitelist.
     */
    event TokenRemoved(address indexed token);

    /**
     * @notice Emitted when a withdrawable token is added.
     * @param token The address of the withdrawable token.
     */
    event WithdrawableTokenAdded(address indexed token);

    /**
     * @notice Emitted when a withdrawable token is removed.
     * @param token The address of the withdrawable token.
     */
    event WithdrawableTokenRemoved(address indexed token);

    /**
     * @notice Emitted when invoker is updated.
     * @param component The address of the invoker component.
     * @param allowed Boolean indicating if the invoker is allowed or not.
     */
    event InvokerUpdated(address indexed component, bool allowed);

    /**
     * @notice Emitted when the holding manager is set.
     * @param oldAddress The previous address of the holding manager.
     * @param newAddress The new address of the holding manager.
     */
    event HoldingManagerUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when a new liquidation manager is requested.
     * @param oldAddress The previous address of the liquidation manager.
     * @param newAddress The new address of the liquidation manager.
     */
    event NewLiquidationManagerRequested(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when the liquidation manager is set.
     * @param oldAddress The previous address of the liquidation manager.
     * @param newAddress The new address of the liquidation manager.
     */
    event LiquidationManagerUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when the stablecoin manager is set.
     * @param oldAddress The previous address of the stablecoin manager.
     * @param newAddress The new address of the stablecoin manager.
     */
    event StablecoinManagerUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when the strategy manager is set.
     * @param oldAddress The previous address of the strategy manager.
     * @param newAddress The new address of the strategy manager.
     */
    event StrategyManagerUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when a new swap manager is requested.
     * @param oldAddress The previous address of the swap manager.
     * @param newAddress The new address of the swap manager.
     */
    event NewSwapManagerRequested(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when the swap manager is set.
     * @param oldAddress The previous address of the swap manager.
     * @param newAddress The new address of the swap manager.
     */
    event SwapManagerUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when the default fee is updated.
     * @param oldFee The previous fee.
     * @param newFee The new fee.
     */
    event PerformanceFeeUpdated(uint256 indexed oldFee, uint256 indexed newFee);

    /**
     * @notice Emitted when the withdraw fee is updated.
     * @param oldFee The previous withdraw fee.
     * @param newFee The new withdraw fee.
     */
    event WithdrawalFeeUpdated(uint256 indexed oldFee, uint256 indexed newFee);

    /**
     * @notice Emitted when the liquidator's bonus is updated.
     * @param oldAmount The previous amount of the liquidator's bonus.
     * @param newAmount The new amount of the liquidator's bonus.
     */
    event LiquidatorBonusUpdated(uint256 oldAmount, uint256 newAmount);

    /**
     * @notice Emitted when the fee address is changed.
     * @param oldAddress The previous fee address.
     * @param newAddress The new fee address.
     */
    event FeeAddressUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when the receipt token factory is updated.
     * @param oldAddress The previous address of the receipt token factory.
     * @param newAddress The new address of the receipt token factory.
     */
    event ReceiptTokenFactoryUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when the liquidity gauge factory is updated.
     * @param oldAddress The previous address of the liquidity gauge factory.
     * @param newAddress The new address of the liquidity gauge factory.
     */
    event LiquidityGaugeFactoryUpdated(address indexed oldAddress, address indexed newAddress);

    /**
     * @notice Emitted when new oracle is requested.
     * @param newOracle The address of the new oracle.
     */
    event NewOracleRequested(address newOracle);

    /**
     * @notice Emitted when the oracle is updated.
     * @param oldOracle The address of the old oracle.
     * @param newOracle The address of the new oracle.
     */
    event OracleUpdated(address indexed oldOracle, address indexed newOracle);

    /**
     * @notice Emitted when oracle data is updated.
     * @param oldData The address of the old oracle data.
     * @param newData The address of the new oracle data.
     */
    event OracleDataUpdated(bytes indexed oldData, bytes indexed newData);

    /**
     * @notice Emitted when a new timelock amount is requested.
     * @param oldVal The previous timelock amount.
     * @param newVal The new timelock amount.
     */
    event TimelockAmountUpdateRequested(uint256 oldVal, uint256 newVal);

    /**
     * @notice Emitted when timelock amount is updated.
     * @param oldVal The previous timelock amount.
     * @param newVal The new timelock amount.
     */
    event TimelockAmountUpdated(uint256 oldVal, uint256 newVal);

    // -- Mappings --

    /**
     * @notice Returns true/false for contracts' whitelist status.
     * @param _contract The address of the contract.
     */
    function isContractWhitelisted(
        address _contract
    ) external view returns (bool);

    /**
     * @notice Returns true if token is whitelisted.
     * @param _token The address of the token.
     */
    function isTokenWhitelisted(
        address _token
    ) external view returns (bool);

    /**
     * @notice Returns true if the token can be withdrawn from a holding.
     * @param _token The address of the token.
     */
    function isTokenWithdrawable(
        address _token
    ) external view returns (bool);

    /**
     * @notice Returns true if caller is allowed invoker.
     * @param _invoker The address of the invoker.
     */
    function allowedInvokers(
        address _invoker
    ) external view returns (bool);

    // -- Essential tokens --

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

    // -- Protocol's stablecoin oracle config --

    /**
     * @notice Oracle contract associated with protocol's stablecoin.
     */
    function jUsdOracle() external view returns (IOracle);

    /**
     * @notice Extra oracle data if needed.
     */
    function oracleData() external view returns (bytes calldata);

    // -- Managers --

    /**
     * @notice Returns the address of the HoldingManager Contract.
     */
    function holdingManager() external view returns (address);

    /**
     * @notice Returns the address of the LiquidationManager Contract.
     */
    function liquidationManager() external view returns (address);

    /**
     * @notice Returns the address of the StablesManager Contract.
     */
    function stablesManager() external view returns (address);

    /**
     * @notice Returns the address of the StrategyManager Contract.
     */
    function strategyManager() external view returns (address);

    /**
     * @notice Returns the address of the SwapManager Contract.
     */
    function swapManager() external view returns (address);

    // -- Fees --

    /**
     * @notice Returns the default performance fee.
     * @dev Uses 2 decimal precision, where 1% is represented as 100.
     */
    function performanceFee() external view returns (uint256);

    /**
     * @notice Returns the maximum performance fee.
     * @dev Uses 2 decimal precision, where 1% is represented as 100.
     */
    function MAX_PERFORMANCE_FEE() external view returns (uint256);

    /**
     * @notice Fee for withdrawing from a holding.
     * @dev Uses 2 decimal precision, where 1% is represented as 100.
     */
    function withdrawalFee() external view returns (uint256);

    /**
     * @notice Returns the maximum withdrawal fee.
     * @dev Uses 2 decimal precision, where 1% is represented as 100.
     */
    function MAX_WITHDRAWAL_FEE() external view returns (uint256);

    /**
     * @notice Returns the fee address, where all the fees are collected.
     */
    function feeAddress() external view returns (address);

    // -- Factories --

    /**
     * @notice Returns the address of the ReceiptTokenFactory.
     */
    function receiptTokenFactory() external view returns (address);

    // -- Utility values --

    /**
     * @notice Minimum allowed jUSD debt amount for a holding to ensure successful liquidation.
     */
    function minDebtAmount() external view returns (uint256);

    /**
     * @notice Returns the collateral rate precision.
     * @dev Should be less than exchange rate precision due to optimization in math.
     */
    function PRECISION() external view returns (uint256);

    /**
     * @notice Returns the exchange rate precision.
     */
    function EXCHANGE_RATE_PRECISION() external view returns (uint256);

    /**
     * @notice Timelock amount in seconds for changing the oracle data.
     */
    function timelockAmount() external view returns (uint256);

    /**
     * @notice Returns the old timelock value for delayed timelock update.
     */
    function oldTimelock() external view returns (uint256);

    /**
     * @notice Returns the new timelock value for delayed timelock update.
     */
    function newTimelock() external view returns (uint256);

    /**
     * @notice Returns the timestamp when the new timelock was requested.
     */
    function newTimelockTimestamp() external view returns (uint256);

    /**
     * @notice Returns the new oracle address for delayed oracle update.
     */
    function newOracle() external view returns (address);

    /**
     * @notice Returns the timestamp when the new oracle was requested.
     */
    function newOracleTimestamp() external view returns (uint256);

    /**
     * @notice Returns the new swap manager address for delayed swap manager update.
     */
    function newSwapManager() external view returns (address);

    /**
     * @notice Returns the timestamp when the new swap manager was requested.
     */
    function newSwapManagerTimestamp() external view returns (uint256);

    /**
     * @notice Returns the new liquidation manager address for delayed liquidation manager update.
     */
    function newLiquidationManager() external view returns (address);

    /**
     * @notice Returns the timestamp when the new liquidation manager was requested.
     */
    function newLiquidationManagerTimestamp() external view returns (uint256);

    // -- Setters --

    /**
     * @notice Whitelists a contract.
     *
     * @notice Requirements:
     * - `_contract` must not be whitelisted.
     *
     * @notice Effects:
     * - Updates the `isContractWhitelisted` mapping.
     *
     * @notice Emits:
     * - `ContractWhitelisted` event indicating successful contract whitelist operation.
     *
     * @param _contract The address of the contract to be whitelisted.
     */
    function whitelistContract(
        address _contract
    ) external;

    /**
     * @notice Blacklists a contract.
     *
     * @notice Requirements:
     * - `_contract` must be whitelisted.
     *
     * @notice Effects:
     * - Updates the `isContractWhitelisted` mapping.
     *
     * @notice Emits:
     * - `ContractBlacklisted` event indicating successful contract blacklist operation.
     *
     * @param _contract The address of the contract to be blacklisted.
     */
    function blacklistContract(
        address _contract
    ) external;

    /**
     * @notice Whitelists a token.
     *
     * @notice Requirements:
     * - `_token` must not be whitelisted.
     *
     * @notice Effects:
     * - Updates the `isTokenWhitelisted` mapping.
     *
     * @notice Emits:
     * - `TokenWhitelisted` event indicating successful token whitelist operation.
     *
     * @param _token The address of the token to be whitelisted.
     */
    function whitelistToken(
        address _token
    ) external;

    /**
     * @notice Removes a token from whitelist.
     *
     * @notice Requirements:
     * - `_token` must be whitelisted.
     *
     * @notice Effects:
     * - Updates the `isTokenWhitelisted` mapping.
     *
     * @notice Emits:
     * - `TokenRemoved` event indicating successful token removal operation.
     *
     * @param _token The address of the token to be whitelisted.
     */
    function removeToken(
        address _token
    ) external;

    /**
     * @notice Registers the `_token` as withdrawable.
     *
     * @notice Requirements:
     * - `msg.sender` must be owner or `strategyManager`.
     * - `_token` must not be withdrawable.
     *
     * @notice Effects:
     * - Updates the `isTokenWithdrawable` mapping.
     *
     * @notice Emits:
     * - `WithdrawableTokenAdded` event indicating successful withdrawable token addition operation.
     *
     * @param _token The address of the token to be added as withdrawable.
     */
    function addWithdrawableToken(
        address _token
    ) external;

    /**
     * @notice Unregisters the `_token` as withdrawable.
     *
     * @notice Requirements:
     * - `_token` must be withdrawable.
     *
     * @notice Effects:
     * - Updates the `isTokenWithdrawable` mapping.
     *
     * @notice Emits:
     * - `WithdrawableTokenRemoved` event indicating successful withdrawable token removal operation.
     *
     * @param _token The address of the token to be removed as withdrawable.
     */
    function removeWithdrawableToken(
        address _token
    ) external;

    /**
     * @notice Sets invoker as allowed or forbidden.
     *
     * @notice Effects:
     * - Updates the `allowedInvokers` mapping.
     *
     * @notice Emits:
     * - `InvokerUpdated` event indicating successful invoker update operation.
     *
     * @param _component Invoker's address.
     * @param _allowed True/false.
     */
    function updateInvoker(address _component, bool _allowed) external;

    /**
     * @notice Sets the Holding Manager Contract's address.
     *
     * @notice Requirements:
     * - `_val` must be different from previous `holdingManager` address.
     *
     * @notice Effects:
     * - Updates the `holdingManager` state variable.
     *
     * @notice Emits:
     * - `HoldingManagerUpdated` event indicating the successful setting of the Holding Manager's address.
     *
     * @param _val The holding manager's address.
     */
    function setHoldingManager(
        address _val
    ) external;

    /**
     * @notice Sets the Liquidation Manager Contract's address.
     *
     * @notice Requirements:
     * - Can only be called once.
     * - `_val` must be non-zero address.
     *
     * @notice Effects:
     * - Updates the `liquidationManager` state variable.
     *
     * @notice Emits:
     * - `LiquidationManagerUpdated` event indicating the successful setting of the Liquidation Manager's address.
     *
     * @param _val The liquidation manager's address.
     */
    function setLiquidationManager(
        address _val
    ) external;

    /**
     * @notice Initiates the process to update the Liquidation Manager Contract's address.
     *
     * @notice Requirements:
     * - `_val` must be non-zero address.
     * - `_val` must be different from previous `liquidationManager` address.
     *
     * @notice Effects:
     * - Updates the the `_newLiquidationManager` state variable.
     * - Updates the the `_newLiquidationManagerTimestamp` state variable.
     *
     * @notice Emits:
     * - `LiquidationManagerUpdateRequested` event indicating successful liquidation manager change request.
     *
     * @param _val The new liquidation manager's address.
     */
    function requestNewLiquidationManager(
        address _val
    ) external;

    /**
     * @notice Sets the Liquidation Manager Contract's address.
     *
     * @notice Requirements:
     * - `_val` must be different from previous `liquidationManager` address.
     * - Timelock must expire.
     *
     * @notice Effects:
     * - Updates the `liquidationManager` state variable.
     * - Updates the the `_newLiquidationManager` state variable.
     * - Updates the the `_newLiquidationManagerTimestamp` state variable.
     *
     * @notice Emits:
     * - `LiquidationManagerUpdated` event indicating the successful setting of the Liquidation Manager's address.
     */
    function acceptNewLiquidationManager() external;

    /**
     * @notice Sets the Stablecoin Manager Contract's address.
     *
     * @notice Requirements:
     * - `_val` must be different from previous `stablesManager` address.
     *
     * @notice Effects:
     * - Updates the `stablesManager` state variable.
     *
     * @notice Emits:
     * - `StablecoinManagerUpdated` event indicating the successful setting of the Stablecoin Manager's address.
     *
     * @param _val The Stablecoin manager's address.
     */
    function setStablecoinManager(
        address _val
    ) external;

    /**
     * @notice Sets the Strategy Manager Contract's address.
     *
     * @notice Requirements:
     * - `_val` must be different from previous `strategyManager` address.
     *
     * @notice Effects:
     * - Updates the `strategyManager` state variable.
     *
     * @notice Emits:
     * - `StrategyManagerUpdated` event indicating the successful setting of the Strategy Manager's address.
     *
     * @param _val The Strategy manager's address.
     */
    function setStrategyManager(
        address _val
    ) external;

    /**
     * @notice Sets the Swap Manager Contract's address.
     *
     * @notice Requirements:
     * - Can only be called once.
     * - `_val` must be non-zero address.
     *
     * @notice Effects:
     * - Updates the `swapManager` state variable.
     *
     * @notice Emits:
     * - `SwapManagerUpdated` event indicating the successful setting of the Swap Manager's address.
     *
     * @param _val The Swap manager's address.
     */
    function setSwapManager(
        address _val
    ) external;

    /**
     * @notice Initiates the process to update the Swap Manager Contract's address.
     *
     * @notice Requirements:
     * - `_val` must be non-zero address.
     * - `_val` must be different from previous `swapManager` address.
     *
     * @notice Effects:
     * - Updates the the `_newSwapManager` state variable.
     * - Updates the the `_newSwapManagerTimestamp` state variable.
     *
     * @notice Emits:
     * - `NewSwapManagerRequested` event indicating successful swap manager change request.
     *
     * @param _val The new swap manager's address.
     */
    function requestNewSwapManager(
        address _val
    ) external;

    /**
     * @notice Updates the Swap Manager Contract    .
     *
     * @notice Requirements:
     * - Timelock must expire.
     *
     * @notice Effects:
     * - Updates the `swapManager` state variable.
     * - Resets `_newSwapManager` to address(0).
     * - Resets `_newSwapManagerTimestamp` to 0.
     *
     * @notice Emits:
     * - `SwapManagerUpdated` event indicating the successful setting of the Swap Manager's address.
     */
    function acceptNewSwapManager() external;

    /**
     * @notice Sets the performance fee.
     *
     * @notice Requirements:
     * - `_val` must be smaller than `FEE_FACTOR` to avoid wrong computations.
     *
     * @notice Effects:
     * - Updates the `performanceFee` state variable.
     *
     * @notice Emits:
     * - `PerformanceFeeUpdated` event indicating successful performance fee update operation.
     *
     * @dev `_val` uses 2 decimal precision, where 1% is represented as 100.
     *
     * @param _val The new performance fee value.
     */
    function setPerformanceFee(
        uint256 _val
    ) external;

    /**
     * @notice Sets the withdrawal fee.
     *
     * @notice Requirements:
     * - `_val` must be smaller than `FEE_FACTOR` to avoid wrong computations.
     *
     * @notice Effects:
     * - Updates the `withdrawalFee` state variable.
     *
     * @notice Emits:
     * - `WithdrawalFeeUpdated` event indicating successful withdrawal fee update operation.
     *
     * @dev `_val` uses 2 decimal precision, where 1% is represented as 100.
     *
     * @param _val The new withdrawal fee value.
     */
    function setWithdrawalFee(
        uint256 _val
    ) external;

    /**
     * @notice Sets the global fee address.
     *
     * @notice Requirements:
     * - `_val` must be different from previous `holdingManager` address.
     *
     * @notice Effects:
     * - Updates the `feeAddress` state variable.
     *
     * @notice Emits:
     * - `FeeAddressUpdated` event indicating successful setting of the global fee address.
     *
     * @param _val The new fee address.
     */
    function setFeeAddress(
        address _val
    ) external;

    /**
     * @notice Sets the receipt token factory's address.
     *
     * @notice Requirements:
     * - `_val` must be different from previous `receiptTokenFactory` address.
     *
     * @notice Effects:
     * - Updates the `receiptTokenFactory` state variable.
     *
     * @notice Emits:
     * - `ReceiptTokenFactoryUpdated` event indicating successful setting of the `receiptTokenFactory` address.
     *
     * @param _factory Receipt token factory's address.
     */
    function setReceiptTokenFactory(
        address _factory
    ) external;

    /**
     * @notice Registers jUSD's oracle change request.
     *
     * @notice Requirements:
     * - Contract must not be in active change.
     *
     * @notice Effects:
     * - Updates the the `_isActiveChange` state variable.
     * - Updates the the `_newOracle` state variable.
     * - Updates the the `_newOracleTimestamp` state variable.
     *
     * @notice Emits:
     * - `NewOracleRequested` event indicating successful jUSD's oracle change request.
     *
     * @param _oracle Liquidity gauge factory's address.
     */
    function requestNewJUsdOracle(
        address _oracle
    ) external;

    /**
     * @notice Updates jUSD's oracle.
     *
     * @notice Requirements:
     * - Contract must be in active change.
     * - Timelock must expire.
     *
     * @notice Effects:
     * - Updates the the `jUsdOracle` state variable.
     * - Updates the the `_isActiveChange` state variable.
     * - Updates the the `_newOracle` state variable.
     * - Updates the the `_newOracleTimestamp` state variable.
     *
     * @notice Emits:
     * - `OracleUpdated` event indicating successful jUSD's oracle change.
     */
    function acceptNewJUsdOracle() external;

    /**
     * @notice Updates the jUSD's oracle data.
     *
     * @notice Requirements:
     * - `_newOracleData` must be different from previous `oracleData`.
     *
     * @notice Effects:
     * - Updates the `oracleData` state variable.
     *
     * @notice Emits:
     * - `OracleDataUpdated` event indicating successful update of the oracle Data.
     *
     * @param _newOracleData New data used for jUSD's oracle data.
     */
    function setJUsdOracleData(
        bytes calldata _newOracleData
    ) external;

    /**
     * @notice Sets the minimum debt amount.
     *
     * @notice Requirements:
     * - `_minDebtAmount` must be greater than zero.
     * - `_minDebtAmount` must be different from previous `minDebtAmount`.
     *
     * @param _minDebtAmount The new minimum debt amount.
     */
    function setMinDebtAmount(
        uint256 _minDebtAmount
    ) external;

    /**
     * @notice Registers timelock change request.
     *
     * @notice Requirements:
     * - `_oldTimelock` must be set zero.
     * - `_newVal` must be greater than zero.
     *
     * @notice Effects:
     * - Updates the the `_oldTimelock` state variable.
     * - Updates the the `_newTimelock` state variable.
     * - Updates the the `_newTimelockTimestamp` state variable.
     *
     * @notice Emits:
     * - `TimelockAmountUpdateRequested` event indicating successful timelock change request.
     *
     * @param _newVal The new timelock value in seconds.
     */
    function requestNewTimelock(
        uint256 _newVal
    ) external;

    /**
     * @notice Updates the timelock amount.
     *
     * @notice Requirements:
     * - Contract must be in active change.
     * - `_newTimelock` must be greater than zero.
     * - The old timelock must expire.
     *
     * @notice Effects:
     * - Updates the the `timelockAmount` state variable.
     * - Updates the the `_oldTimelock` state variable.
     * - Updates the the `_newTimelock` state variable.
     * - Updates the the `_newTimelockTimestamp` state variable.
     *
     * @notice Emits:
     * - `TimelockAmountUpdated` event indicating successful timelock amount change.
     */
    function acceptNewTimelock() external;

    // -- Getters --

    /**
     * @notice Returns the up to date exchange rate of the protocol's stablecoin jUSD.
     *
     * @notice Requirements:
     * - Oracle must have updated rate.
     * - Rate must be a non zero positive value.
     *
     * @return The current exchange rate.
     */
    function getJUsdExchangeRate() external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IOracle } from "../oracle/IOracle.sol";
import { IManager } from "./IManager.sol";

/**
 * @title ISharesRegistry
 * @dev Interface for the Shares Registry Contract.
 * @dev Based on MIM CauldraonV2 contract.
 */
interface ISharesRegistry {
    /**
     * @notice Configuration struct for registry parameters.
     * @dev Used to store key parameters that control collateral and liquidation behavior.
     *
     * @param collateralizationRate The minimum collateral ratio required, expressed as a percentage with precision.
     * @param liquidationBuffer Is a value, that represents the buffer between the collateralization rate and the
     * liquidation threshold, upon which the liquidation is allowed.
     * @param liquidatorBonus The bonus percentage given to liquidators as incentive, expressed with precision.
     */
    struct RegistryConfig {
        uint256 collateralizationRate;
        uint256 liquidationBuffer;
        uint256 liquidatorBonus;
    }

    /**
     * @notice Event emitted when borrowed amount is set.
     * @param _holding The address of the holding.
     * @param oldVal The old value.
     * @param newVal The new value.
     */
    event BorrowedSet(address indexed _holding, uint256 oldVal, uint256 newVal);

    /**
     * @notice Event emitted when collateral is registered.
     * @param user The address of the user.
     * @param share The amount of shares.
     */
    event CollateralAdded(address indexed user, uint256 share);

    /**
     * @notice Event emitted when collateral was unregistered.
     * @param user The address of the user.
     * @param share The amount of shares.
     */
    event CollateralRemoved(address indexed user, uint256 share);

    /**
     * @notice Event emitted when the collateralization rate is updated.
     * @param oldVal The old value.
     * @param newVal The new value.
     */
    event CollateralizationRateUpdated(uint256 oldVal, uint256 newVal);

    /**
     * @notice Event emitted when a new oracle is requested.
     * @param newOracle The new oracle address.
     */
    event NewOracleRequested(address newOracle);

    /**
     * @notice Event emitted when the oracle is updated.
     */
    event OracleUpdated();

    /**
     * @notice Event emitted when new oracle data is requested.
     * @param newData The new data.
     */
    event NewOracleDataRequested(bytes newData);

    /**
     * @notice Event emitted when oracle data is updated.
     */
    event OracleDataUpdated();

    /**
     * @notice Event emitted when a new timelock amount is requested.
     * @param oldVal The old value.
     * @param newVal The new value.
     */
    event TimelockAmountUpdateRequested(uint256 oldVal, uint256 newVal);

    /**
     * @notice Event emitted when timelock amount is updated.
     * @param oldVal The old value.
     * @param newVal The new value.
     */
    event TimelockAmountUpdated(uint256 oldVal, uint256 newVal);

    /**
     * @notice Event emitted when the config is updated.
     * @param token The token address.
     * @param oldVal The old config.
     * @param newVal The new config.
     */
    event ConfigUpdated(address indexed token, RegistryConfig oldVal, RegistryConfig newVal);

    /**
     * @notice Returns holding's borrowed amount.
     * @param _holding The address of the holding.
     * @return The borrowed amount.
     */
    function borrowed(
        address _holding
    ) external view returns (uint256);

    /**
     * @notice Returns holding's available collateral amount.
     * @param _holding The address of the holding.
     * @return The collateral amount.
     */
    function collateral(
        address _holding
    ) external view returns (uint256);

    /**
     * @notice Returns the token address for which this registry was created.
     * @return The token address.
     */
    function token() external view returns (address);

    /**
     * @notice Contract that contains all the necessary configs of the protocol.
     * @return The manager contract.
     */
    function manager() external view returns (IManager);

    /**
     * @notice Oracle contract associated with this share registry.
     * @return The oracle contract.
     */
    function oracle() external view returns (IOracle);

    /**
     * @notice Extra oracle data if needed.
     * @return The oracle data.
     */
    function oracleData() external view returns (bytes calldata);

    /**
     * @notice Current timelock amount.
     * @return The timelock amount.
     */
    function timelockAmount() external view returns (uint256);

    // -- User specific methods --

    /**
     * @notice Updates `_holding`'s borrowed amount.
     *
     * @notice Requirements:
     * - `msg.sender` must be the Stables Manager Contract.
     * - `_newVal` must be greater than or equal to the minimum debt amount.
     *
     * @notice Effects:
     * - Updates `borrowed` mapping.
     *
     * @notice Emits:
     * - `BorrowedSet` indicating holding's borrowed amount update operation.
     *
     * @param _holding The address of the user's holding.
     * @param _newVal The new borrowed amount.
     */
    function setBorrowed(address _holding, uint256 _newVal) external;

    /**
     * @notice Registers collateral for user's `_holding`.
     *
     * @notice Requirements:
     * - `msg.sender` must be the Stables Manager Contract.
     *
     * @notice Effects:
     * - Updates `collateral` mapping.
     *
     * @notice Emits:
     * - `CollateralAdded` event indicating collateral addition operation.
     *
     * @param _holding The address of the user's holding.
     * @param _share The new collateral shares.
     */
    function registerCollateral(address _holding, uint256 _share) external;

    /**
     * @notice Registers a collateral removal operation for user's `_holding`.
     *
     * @notice Requirements:
     * - `msg.sender` must be the Stables Manager Contract.
     *
     * @notice Effects:
     * - Updates `collateral` mapping.
     *
     * @notice Emits:
     * - `CollateralRemoved` event indicating collateral removal operation.
     *
     * @param _holding The address of the user's holding.
     * @param _share The new collateral shares.
     */
    function unregisterCollateral(address _holding, uint256 _share) external;

    // -- Administration --

    /**
     * @notice Updates the registry configuration parameters.
     *
     * @notice Effects:
     * - Updates `config` state variable.
     *
     * @notice Emits:
     * - `ConfigUpdated` event indicating config update operation.
     *
     * @param _newConfig The new configuration parameters.
     */
    function updateConfig(
        RegistryConfig memory _newConfig
    ) external;

    /**
     * @notice Requests a change for the oracle address.
     *
     * @notice Requirements:
     * - Previous oracle change request must have expired or been accepted.
     * - No timelock or oracle data change requests should be active.
     * - `_oracle` must not be the zero address.
     *
     * @notice Effects:
     * - Updates `_isOracleActiveChange` state variable.
     * - Updates `_newOracle` state variable.
     * - Updates `_newOracleTimestamp` state variable.
     *
     * @notice Emits:
     * - `NewOracleRequested` event indicating new oracle request.
     *
     * @param _oracle The new oracle address.
     */
    function requestNewOracle(
        address _oracle
    ) external;

    /**
     * @notice Updates the oracle.
     *
     * @notice Requirements:
     * - Oracle change must have been requested and the timelock must have passed.
     *
     * @notice Effects:
     * - Updates `oracle` state variable.
     * - Updates `_isOracleActiveChange` state variable.
     * - Updates `_newOracle` state variable.
     * - Updates `_newOracleTimestamp` state variable.
     *
     * @notice Emits:
     * - `OracleUpdated` event indicating oracle update.
     */
    function setOracle() external;

    /**
     * @notice Requests a change for oracle data.
     *
     * @notice Requirements:
     * - Previous oracle data change request must have expired or been accepted.
     * - No timelock or oracle change requests should be active.
     *
     * @notice Effects:
     * - Updates `_isOracleDataActiveChange` state variable.
     * - Updates `_newOracleData` state variable.
     * - Updates `_newOracleDataTimestamp` state variable.
     *
     * @notice Emits:
     * - `NewOracleDataRequested` event indicating new oracle data request.
     *
     * @param _data The new oracle data.
     */
    function requestNewOracleData(
        bytes calldata _data
    ) external;

    /**
     * @notice Updates the oracle data.
     *
     * @notice Requirements:
     * - Oracle data change must have been requested and the timelock must have passed.
     *
     * @notice Effects:
     * - Updates `oracleData` state variable.
     * - Updates `_isOracleDataActiveChange` state variable.
     * - Updates `_newOracleData` state variable.
     * - Updates `_newOracleDataTimestamp` state variable.
     *
     * @notice Emits:
     * - `OracleDataUpdated` event indicating oracle data update.
     */
    function setOracleData() external;

    /**
     * @notice Requests a timelock update.
     *
     * @notice Requirements:
     * - `_newVal` must not be zero.
     * - Previous timelock change request must have expired or been accepted.
     * - No oracle or oracle data change requests should be active.
     *
     * @notice Effects:
     * - Updates `_isTimelockActiveChange` state variable.
     * - Updates `_oldTimelock` state variable.
     * - Updates `_newTimelock` state variable.
     * - Updates `_newTimelockTimestamp` state variable.
     *
     * @notice Emits:
     * - `TimelockAmountUpdateRequested` event indicating timelock change request.
     *
     * @param _newVal The new value in seconds.
     */
    function requestTimelockAmountChange(
        uint256 _newVal
    ) external;

    /**
     * @notice Updates the timelock amount.
     *
     * @notice Requirements:
     * - Timelock change must have been requested and the timelock must have passed.
     * - The timelock for timelock change must have already expired.
     *
     * @notice Effects:
     * - Updates `timelockAmount` state variable.
     * - Updates `_oldTimelock` state variable.
     * - Updates `_newTimelock` state variable.
     * - Updates `_newTimelockTimestamp` state variable.
     *
     * @notice Emits:
     * - `TimelockAmountUpdated` event indicating timelock amount change operation.
     */
    function acceptTimelockAmountChange() external;

    // -- Getters --

    /**
     * @notice Returns the up to date exchange rate of the `token`.
     *
     * @notice Requirements:
     * - Oracle must provide an updated rate.
     *
     * @return The updated exchange rate.
     */
    function getExchangeRate() external view returns (uint256);

    /**
     * @notice Returns the configuration parameters for the registry.
     * @return The RegistryConfig struct containing the parameters.
     */
    function getConfig() external view returns (RegistryConfig memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IJigsawUSD } from "../core/IJigsawUSD.sol";
import { ISharesRegistry } from "../core/ISharesRegistry.sol";
import { IManager } from "./IManager.sol";

/**
 * @title IStablesManager
 * @notice Interface for the Stables Manager.
 */
interface IStablesManager {
    // -- Custom types --

    /**
     * @notice Structure to store state and deployment address for a share registry
     */
    struct ShareRegistryInfo {
        bool active; // Flag indicating if the registry is active
        address deployedAt; // Address where the registry is deployed
    }

    /**
     * @notice Temporary struct used to store data during borrow operations to avoid stack too deep errors.
     * @dev This struct helps organize variables used in the borrow function.
     * @param registry The shares registry contract for the collateral token
     * @param exchangeRatePrecision The precision used for exchange rate calculations
     * @param amount The normalized amount (18 decimals) of collateral being borrowed against
     * @param amountValue The USD value of the collateral amount
     */
    struct BorrowTempData {
        ISharesRegistry registry;
        uint256 exchangeRatePrecision;
        uint256 amount;
        uint256 amountValue;
    }

    // -- Events --

    /**
     * @notice Emitted when collateral is registered.
     * @param holding The address of the holding.
     * @param token The address of the token.
     * @param amount The amount of collateral.
     */
    event AddedCollateral(address indexed holding, address indexed token, uint256 amount);

    /**
     * @notice Emitted when collateral is unregistered.
     * @param holding The address of the holding.
     * @param token The address of the token.
     * @param amount The amount of collateral.
     */
    event RemovedCollateral(address indexed holding, address indexed token, uint256 amount);

    /**
     * @notice Emitted when a borrow action is performed.
     * @param holding The address of the holding.
     * @param jUsdMinted The amount of jUSD minted.
     * @param mintToUser Boolean indicating if the amount is minted directly to the user.
     */
    event Borrowed(address indexed holding, uint256 jUsdMinted, bool mintToUser);

    /**
     * @notice Emitted when a repay action is performed.
     * @param holding The address of the holding.
     * @param amount The amount repaid.
     * @param burnFrom The address to burn from.
     */
    event Repaid(address indexed holding, uint256 amount, address indexed burnFrom);

    /**
     * @notice Emitted when a registry is added.
     * @param token The address of the token.
     * @param registry The address of the registry.
     */
    event RegistryAdded(address indexed token, address indexed registry);

    /**
     * @notice Emitted when a registry is updated.
     * @param token The address of the token.
     * @param registry The address of the registry.
     */
    event RegistryUpdated(address indexed token, address indexed registry);

    /**
     * @notice Returns total borrowed jUSD amount using `token`.
     * @param _token The address of the token.
     * @return The total borrowed amount.
     */
    function totalBorrowed(
        address _token
    ) external view returns (uint256);

    /**
     * @notice Returns config info for each token.
     * @param _token The address of the token to get registry info for.
     * @return Boolean indicating if the registry is active and the address of the registry.
     */
    function shareRegistryInfo(
        address _token
    ) external view returns (bool, address);

    /**
     * @notice Returns protocol's stablecoin address.
     * @return The address of the Jigsaw stablecoin.
     */
    function jUSD() external view returns (IJigsawUSD);

    /**
     * @notice Contract that contains all the necessary configs of the protocol.
     * @return The manager contract.
     */
    function manager() external view returns (IManager);

    // -- User specific methods --

    /**
     * @notice Registers new collateral.
     *
     * @dev The amount will be transformed to shares.
     *
     * @notice Requirements:
     * - The caller must be allowed to perform this action directly. If user - use Holding Manager Contract.
     * - The `_token` must be whitelisted.
     * - The `_token`'s registry must be active.
     *
     * @notice Effects:
     * - Adds collateral for the holding.
     *
     * @notice Emits:
     * - `AddedCollateral` event indicating successful collateral addition operation.
     *
     * @param _holding The holding for which collateral is added.
     * @param _token Collateral token.
     * @param _amount Amount of tokens to be added as collateral.
     */
    function addCollateral(address _holding, address _token, uint256 _amount) external;

    /**
     * @notice Unregisters collateral.
     *
     * @notice Requirements:
     * - The contract must not be paused.
     * - The caller must be allowed to perform this action directly. If user - use Holding Manager Contract.
     * - The token's registry must be active.
     * - `_holding` must stay solvent after collateral removal.
     *
     * @notice Effects:
     * - Removes collateral for the holding.
     *
     * @notice Emits:
     * - `RemovedCollateral` event indicating successful collateral removal operation.
     *
     * @param _holding The holding for which collateral is removed.
     * @param _token Collateral token.
     * @param _amount Amount of collateral.
     */
    function removeCollateral(address _holding, address _token, uint256 _amount) external;

    /**
     * @notice Unregisters collateral.
     *
     * @notice Requirements:
     * - The caller must be the LiquidationManager.
     * - The token's registry must be active.
     *
     * @notice Effects:
     * - Force removes collateral from the `_holding` in case of liquidation, without checking if user is solvent after
     * collateral removal.
     *
     * @notice Emits:
     * - `RemovedCollateral` event indicating successful collateral removal operation.
     *
     * @param _holding The holding for which collateral is added.
     * @param _token Collateral token.
     * @param _amount Amount of collateral.
     */
    function forceRemoveCollateral(address _holding, address _token, uint256 _amount) external;

    /**
     * @notice Mints stablecoin to the user.
     *
     * @notice Requirements:
     * - The caller must be allowed to perform this action directly. If user - use Holding Manager Contract.
     * - The token's registry must be active.
     * - `_amount` must be greater than zero.
     *
     * @notice Effects:
     * - Mints stablecoin based on the collateral amount.
     * - Updates the total borrowed jUSD amount for `_token`, used for borrowing.
     * - Updates `_holdings`'s borrowed amount in `token`'s registry contract.
     * - Ensures the holding remains solvent.
     *
     * @notice Emits:
     * - `Borrowed`.
     *
     * @param _holding The holding for which collateral is added.
     * @param _token Collateral token.
     * @param _amount The collateral amount equivalent for borrowed jUSD.
     * @param _minJUsdAmountOut The minimum amount of jUSD that is expected to be received.
     * @param _mintDirectlyToUser If true, mints to user instead of holding.
     *
     * @return jUsdMintAmount The amount of jUSD minted.
     */
    function borrow(
        address _holding,
        address _token,
        uint256 _amount,
        uint256 _minJUsdAmountOut,
        bool _mintDirectlyToUser
    ) external returns (uint256 jUsdMintAmount);

    /**
     * @notice Repays debt.
     *
     * @notice Requirements:
     * - The caller must be allowed to perform this action directly. If user - use Holding Manager Contract.
     * - The token's registry must be active.
     * - The holding must have a positive borrowed amount.
     * - `_amount` must not exceed `holding`'s borrowed amount.
     * - `_amount` must be greater than zero.
     * - `_burnFrom` must not be the zero address.
     *
     * @notice Effects:
     * - Updates the total borrowed jUSD amount for `_token`, used for borrowing.
     * - Updates `_holdings`'s borrowed amount in `token`'s registry contract.
     * - Burns `_amount` jUSD tokens from `_burnFrom` address
     *
     * @notice Emits:
     * - `Repaid` event indicating successful repay operation.
     *
     * @param _holding The holding for which repay is performed.
     * @param _token Collateral token.
     * @param _amount The repaid jUSD amount.
     * @param _burnFrom The address to burn from.
     */
    function repay(address _holding, address _token, uint256 _amount, address _burnFrom) external;

    // -- Administration --

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

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

    // -- Getters --

    /**
     * @notice Returns true if user is solvent for the specified token.
     *
     * @dev The method reverts if block.timestamp - _maxTimeRange > exchangeRateUpdatedAt.
     *
     * @notice Requirements:
     * - `_holding` must not be the zero address.
     * - There must be registry for `_token`.
     *
     * @param _token The token for which the check is done.
     * @param _holding The user address.
     *
     * @return flag indicating whether `holding` is solvent.
     */
    function isSolvent(address _token, address _holding) external view returns (bool);

    /**
     * @notice Checks if a holding can be liquidated for a specific token.
     *
     * @notice Requirements:
     * - `_holding` must not be the zero address.
     * - There must be registry for `_token`.
     *
     * @param _token The token for which the check is done.
     * @param _holding The user address.
     *
     * @return flag indicating whether `holding` is liquidatable.
     */
    function isLiquidatable(address _token, address _holding) external view returns (bool);

    /**
     * @notice Computes the solvency ratio.
     *
     * @dev Solvency ratio is calculated based on the used collateral type, its collateralization and exchange rates,
     * and `_holding`'s borrowed amount.
     *
     * @param _holding The holding address to check for.
     * @param registry The Shares Registry Contract for the token.
     * @param rate The rate to compute ratio for (either collateralization rate for `isSolvent` or liquidation
     * threshold for `isLiquidatable`).
     *
     * @return The calculated solvency ratio.
     */
    function getRatio(address _holding, ISharesRegistry registry, uint256 rate) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IReceiptToken } from "../core/IReceiptToken.sol";

/**
 * @title IStrategy
 * @notice Interface for a Strategies.
 *
 * @dev This interface defines the standard functions and events for a strategy contract.
 * @dev The strategy allows for the deposit, withdrawal, and reward claiming functionalities.
 * @dev It also provides views for essential information about the strategy's token and rewards.
 */
interface IStrategy {
    // -- Custom types --

    /**
     * @notice Struct containing parameters for a withdrawal operation.
     * @param shares The number of shares to withdraw.
     * @param totalShares The total shares owned by the user.
     * @param shareRatio The ratio of the shares to withdraw relative to the total shares owned by the user.
     * @param shareDecimals The number of decimals of the strategy's shares.
     * @param investment The amount of initial investment corresponding to the shares being withdrawn.
     * @param assetsToWithdraw The underlying assets withdrawn by the user, including yield and excluding fee.
     * @param balanceBefore The user's `tokenOut` balance before the withdrawal transaction.
     * @param withdrawnAmount The amount of underlying assets withdrawn by the user, excluding fee.
     * @param yield The yield generated by the strategy excluding fee, as fee are taken from the yield.
     * @param fee The amount of fee taken by the protocol.
     */
    struct WithdrawParams {
        uint256 shares;
        uint256 totalShares;
        uint256 shareRatio;
        uint256 shareDecimals;
        uint256 investment;
        uint256 assetsToWithdraw;
        uint256 balanceBefore;
        uint256 withdrawnAmount;
        int256 yield;
        uint256 fee;
    }

    /**
     * @notice Emitted when funds are deposited.
     *
     * @param asset The address of the asset.
     * @param tokenIn The address of the input token.
     * @param assetAmount The amount of the asset.
     * @param tokenInAmount The amount of the input token.
     * @param shares The number of shares received.
     * @param recipient The address of the recipient.
     */
    event Deposit(
        address indexed asset,
        address indexed tokenIn,
        uint256 assetAmount,
        uint256 tokenInAmount,
        uint256 shares,
        address indexed recipient
    );

    /**
     * @notice Emitted when funds are withdrawn.
     *
     * @param asset The address of the asset.
     * @param recipient The address of the recipient.
     * @param shares The number of shares withdrawn.
     * @param withdrawnAmount The amount of the asset withdrawn.
     * @param yield The amount of yield generated by the user beyond their initial investment.
     */
    event Withdraw(
        address indexed asset,
        address indexed recipient,
        uint256 shares,
        uint256 withdrawnAmount,
        uint256 initialInvestment,
        int256 yield
    );

    /**
     * @notice Emitted when rewards are claimed.
     *
     * @param recipient The address of the recipient.
     * @param rewards The array of reward amounts.
     * @param rewardTokens The array of reward token addresses.
     */
    event Rewards(address indexed recipient, uint256[] rewards, address[] rewardTokens);

    /**
     * @notice Returns investments details.
     * @param _recipient The address of the recipient.
     * @return investedAmount The amount invested.
     * @return totalShares The total shares.
     */
    function recipients(
        address _recipient
    ) external view returns (uint256 investedAmount, uint256 totalShares);

    /**
     * @notice Returns the address of the token accepted by the strategy's underlying protocol as input.
     * @return tokenIn The address of the tokenIn.
     */
    function tokenIn() external view returns (address);

    /**
     * @notice Returns the address of token issued by the strategy's underlying protocol after deposit.
     * @return tokenOut The address of the tokenOut.
     */
    function tokenOut() external view returns (address);

    /**
     * @notice Returns the address of the strategy's main reward token.
     * @return rewardToken The address of the reward token.
     */
    function rewardToken() external view returns (address);

    /**
     * @notice Returns the address of the receipt token minted by the strategy itself.
     * @return receiptToken The address of the receipt token.
     */
    function receiptToken() external view returns (IReceiptToken);

    /**
     * @notice Returns the number of decimals of the strategy's shares.
     * @return sharesDecimals The number of decimals.
     */
    function sharesDecimals() external view returns (uint256);

    /**
     * @notice Returns the address of the receipt token.
     * @return receiptTokenAddress The address of the receipt token.
     */
    function getReceiptTokenAddress() external view returns (address receiptTokenAddress);

    /**
     * @notice Deposits funds into the strategy.
     *
     * @dev Some strategies won't give back any receipt tokens; in this case 'tokenOutAmount' will be 0.
     * 'tokenInAmount' will be equal to '_amount' in case the '_asset' is the same as strategy 'tokenIn()'.
     *
     * @param _asset The token to be invested.
     * @param _amount The token's amount.
     * @param _recipient The address of the recipient.
     * @param _data Extra data.
     *
     * @return tokenOutAmount The receipt tokens amount/obtained shares.
     * @return tokenInAmount The returned token in amount.
     */
    function deposit(
        address _asset,
        uint256 _amount,
        address _recipient,
        bytes calldata _data
    ) external returns (uint256 tokenOutAmount, uint256 tokenInAmount);

    /**
     * @notice Withdraws deposited funds.
     *
     * @param _shares The amount to withdraw.
     * @param _recipient The address of the recipient.
     * @param _asset The token to be withdrawn.
     * @param _data Extra data.
     *
     * @return withdrawnAmount The actual amount of asset withdrawn from the strategy.
     * @return initialInvestment The amount of initial investment.
     * @return yield The amount of yield generated by the user beyond their initial investment.
     * @return fee The amount of fee charged by the strategy.
     */
    function withdraw(
        uint256 _shares,
        address _recipient,
        address _asset,
        bytes calldata _data
    ) external returns (uint256 withdrawnAmount, uint256 initialInvestment, int256 yield, uint256 fee);

    /**
     * @notice Claims rewards from the strategy.
     *
     * @param _recipient The address of the recipient.
     * @param _data Extra data.
     *
     * @return amounts The reward tokens amounts.
     * @return tokens The reward tokens addresses.
     */
    function claimRewards(
        address _recipient,
        bytes calldata _data
    ) external returns (uint256[] memory amounts, address[] memory tokens);

    /**
     * @notice Participants info.
     */
    struct RecipientInfo {
        uint256 investedAmount;
        uint256 totalShares;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IStrategy } from ".//IStrategy.sol";
import { IManager } from "./IManager.sol";
import { IStrategyManagerMin } from "./IStrategyManagerMin.sol";

/**
 * @title IStrategyManager
 * @dev Interface for the StrategyManager contract.
 */
interface IStrategyManager is IStrategyManagerMin {
    // -- Custom Types --

    /**
     * @notice Contains details about a specific strategy, such as its performance fee, active status, and whitelisted
     * status.
     * @param performanceFee fee charged as a percentage of the profits generated by the strategy.
     * @param active flag indicating whether the strategy is active.
     * @param whitelisted flag indicating whether strategy is approved for investment.
     */
    struct StrategyInfo {
        uint256 performanceFee;
        bool active;
        bool whitelisted;
    }

    /**
     * @notice Contains data required for moving investment from one strategy to another.
     * @param strategyFrom strategy's address where investment is taken from.
     * @param strategyTo strategy's address where to invest.
     * @param shares investment amount.
     * @param dataFrom data required by `strategyFrom` to perform `_claimInvestment`.
     * @param dataTo data required by `strategyTo` to perform `_invest`.
     * @param strategyToMinSharesAmountOut minimum amount of shares to receive.
     */
    struct MoveInvestmentData {
        address strategyFrom;
        address strategyTo;
        uint256 shares;
        bytes dataFrom;
        bytes dataTo;
        uint256 strategyToMinSharesAmountOut;
    }

    /**
     * @dev Struct used for _claimInvestment function
     * @param strategyContract The strategy contract instance being interacted with
     * @param withdrawnAmount The amount of the asset withdrawn from the strategy
     * @param initialInvestment The amount of initial investment
     * @param yield The yield amount (positive for profit, negative for loss)
     * @param fee The amount of fee charged by the strategy
     * @param remainingShares The number of shares remaining after the withdrawal
     */
    struct ClaimInvestmentData {
        IStrategy strategyContract;
        uint256 withdrawnAmount;
        uint256 initialInvestment;
        int256 yield;
        uint256 fee;
        uint256 remainingShares;
    }

    // -- Events --

    /**
     * @notice Emitted when a new strategy is added to the whitelist.
     * @param strategy The address of the strategy that was added.
     */
    event StrategyAdded(address indexed strategy);

    /**
     * @notice Emitted when an existing strategy is removed from the whitelist.
     * @param strategy The address of the strategy that was removed.
     */
    event StrategyRemoved(address indexed strategy);

    /**
     * @notice Emitted when an existing strategy info is updated.
     * @param strategy The address of the strategy that was updated.
     * @param active Indicates if the strategy is active.
     * @param fee The fee associated with the strategy.
     */
    event StrategyUpdated(address indexed strategy, bool active, uint256 fee);

    /**
     * @notice Emitted when an investment is created.
     * @param holding The address of the holding.
     * @param user The address of the user.
     * @param token The address of the token invested.
     * @param strategy The address of the strategy used for investment.
     * @param amount The amount of tokens invested.
     * @param tokenOutResult The result amount of the output token.
     * @param tokenInResult The result amount of the input token.
     */
    event Invested(
        address indexed holding,
        address indexed user,
        address indexed token,
        address strategy,
        uint256 amount,
        uint256 tokenOutResult,
        uint256 tokenInResult
    );

    /**
     * @notice Emitted when an investment is moved between strategies.
     * @param holding The address of the holding.
     * @param user The address of the user.
     * @param token The address of the token invested.
     * @param strategyFrom The address of the strategy from which the investment is moved.
     * @param strategyTo The address of the strategy to which the investment is moved.
     * @param shares The amount of shares moved.
     * @param tokenOutResult The result amount of the output token.
     * @param tokenInResult The result amount of the input token.
     */
    event InvestmentMoved(
        address indexed holding,
        address indexed user,
        address indexed token,
        address strategyFrom,
        address strategyTo,
        uint256 shares,
        uint256 tokenOutResult,
        uint256 tokenInResult
    );

    /**
     * @notice Emitted when collateral is adjusted from a claim investment or claim rewards operation.
     * @param holding The address of the holding.
     * @param token The address of the token.
     * @param value The value of the collateral adjustment.
     * @param add Indicates if the collateral is added (true) or removed (false).
     */
    event CollateralAdjusted(address indexed holding, address indexed token, uint256 value, bool add);

    /**
     * @notice Emitted when an investment is withdrawn.
     * @param holding The address of the holding.
     * @param user The address of the user.
     * @param token The address of the token withdrawn.
     * @param strategy The address of the strategy from which the investment is withdrawn.
     * @param shares The amount of shares withdrawn.
     * @param withdrawnAmount The amount of tokens withdrawn.
     * @param initialInvestment The amount of initial investment.
     * @param yield The yield amount (positive for profit, negative for loss)
     * @param fee The amount of fee charged by the strategy
     */
    event StrategyClaim(
        address indexed holding,
        address indexed user,
        address indexed token,
        address strategy,
        uint256 shares,
        uint256 withdrawnAmount,
        uint256 initialInvestment,
        int256 yield,
        uint256 fee
    );

    /**
     * @notice Emitted when rewards are claimed.
     * @param token The address of the token rewarded.
     * @param holding The address of the holding.
     * @param amount The amount of rewards claimed.
     */
    event RewardsClaimed(address indexed token, address indexed holding, uint256 amount);

    /**
     * @notice Contract that contains all the necessary configs of the protocol.
     * @return The manager contract.
     */
    function manager() external view returns (IManager);

    // -- User specific methods --

    /**
     * @notice Invests `_token` into `_strategy`.
     *
     * @notice Requirements:
     * - Strategy must be whitelisted.
     * - Amount must be non-zero.
     * - Token specified for investment must be whitelisted.
     * - Msg.sender must have holding.
     *
     * @notice Effects:
     * - Performs investment to the specified `_strategy`.
     * - Deposits holding's collateral to the specified `_strategy`.
     * - Adds `_strategy` used for investment to the holdingToStrategy data structure.
     *
     * @notice Emits:
     * - Invested event indicating successful investment operation.
     *
     * @param _token address.
     * @param _strategy address.
     * @param _amount to be invested.
     * @param _minSharesAmountOut minimum amount of shares to receive.
     * @param _data needed by each individual strategy.
     *
     * @return tokenOutAmount receipt tokens amount.
     * @return tokenInAmount tokenIn amount.
     */
    function invest(
        address _token,
        address _strategy,
        uint256 _amount,
        uint256 _minSharesAmountOut,
        bytes calldata _data
    ) external returns (uint256 tokenOutAmount, uint256 tokenInAmount);

    /**
     * @notice Claims investment from one strategy and invests it into another.
     *
     * @notice Requirements:
     * - The `strategyFrom` and `strategyTo` must be valid and active.
     * - The `strategyFrom` and `strategyTo` must be different.
     * - Msg.sender must have a holding.
     *
     * @notice Effects:
     * - Claims the investment from `strategyFrom`.
     * - Invests the claimed amount into `strategyTo`.
     *
     * @notice Emits:
     * - InvestmentMoved event indicating successful investment movement operation.
     *
     * @dev Some strategies won't give back any receipt tokens; in this case 'tokenOutAmount' will be 0.
     * @dev 'tokenInAmount' will be equal to '_amount' in case the '_asset' is the same as strategy 'tokenIn()'.
     *
     * @param _token The address of the token.
     * @param _data The MoveInvestmentData object containing strategy and amount details.
     *
     * @return tokenOutAmount The amount of receipt tokens returned.
     * @return tokenInAmount The amount of tokens invested in the new strategy.
     */
    function moveInvestment(
        address _token,
        MoveInvestmentData calldata _data
    ) external returns (uint256 tokenOutAmount, uint256 tokenInAmount);

    /**
     * @notice Claims a strategy investment.
     *
     * @notice Requirements:
     * - The `_strategy` must be valid.
     * - Msg.sender must be allowed to execute the call.
     * - `_shares` must be of valid amount.
     * - Specified `_holding` must exist within protocol.
     *
     * @notice Effects:
     * - Withdraws investment from `_strategy`.
     * - Updates `holdingToStrategy` if needed.
     *
     * @notice Emits:
     * - StrategyClaim event indicating successful claim operation.
     *
     * @dev Withdraws investment from a strategy.
     * @dev Some strategies will allow only the tokenIn to be withdrawn.
     * @dev 'AssetAmount' will be equal to 'tokenInAmount' in case the '_asset' is the same as strategy 'tokenIn()'.
     *
     * @param _holding holding's address.
     * @param _token address to be received.
     * @param _strategy strategy to invest into.
     * @param _shares shares amount.
     * @param _data extra data.
     *
     * @return withdrawnAmount The amount of tokens withdrawn.
     * @return initialInvestment The amount of initial investment.
     * @return yield The yield amount (positive for profit, negative for loss)
     * @return fee The amount of fee charged by the strategy
     */
    function claimInvestment(
        address _holding,
        address _token,
        address _strategy,
        uint256 _shares,
        bytes calldata _data
    ) external returns (uint256 withdrawnAmount, uint256 initialInvestment, int256 yield, uint256 fee);

    /**
     * @notice Claims rewards from strategy.
     *
     * @notice Requirements:
     * - The `_strategy` must be valid.
     * - Msg.sender must have valid holding within protocol.
     *
     * @notice Effects:
     * - Claims rewards from strategies.
     * - Adds accrued rewards as a collateral for holding.
     *
     * @param _strategy strategy to invest into.
     * @param _data extra data.
     *
     * @return rewards reward amounts.
     * @return tokens reward tokens.
     */
    function claimRewards(
        address _strategy,
        bytes calldata _data
    ) external returns (uint256[] memory rewards, address[] memory tokens);

    // -- Administration --

    /**
     * @notice Adds a new strategy to the whitelist.
     * @param _strategy strategy's address.
     */
    function addStrategy(
        address _strategy
    ) external;

    /**
     * @notice Updates an existing strategy info.
     * @param _strategy strategy's address.
     * @param _info info.
     */
    function updateStrategy(address _strategy, StrategyInfo calldata _info) external;

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

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

    // -- Getters --

    /**
     * @notice Returns all the strategies holding has invested in.
     * @dev Should be only called off-chain as can be high gas consuming.
     * @param _holding address for which the strategies are requested.
     */
    function getHoldingToStrategy(
        address _holding
    ) external view returns (address[] memory);

    /**
     * @notice Returns the number of strategies the holding has invested in.
     * @param _holding address for which the strategy count is requested.
     * @return uint256 The number of strategies the holding has invested in.
     */
    function getHoldingToStrategyLength(
        address _holding
    ) external view returns (uint256);
}

// 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;
    }
}

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

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

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IOracle {
    // -- State variables --

    /**
     * @notice Returns the address of the token the oracle is for.
     */
    function underlying() external view returns (address);

    // -- Functions --

    /**
     * @notice Returns a human readable name of the underlying of the oracle.
     */
    function name() external view returns (string memory);

    /**
     * @notice Returns a human readable symbol of the underlying of the oracle.
     */
    function symbol() external view returns (string memory);

    /**
     * @notice Check the last exchange rate without any state changes.
     *
     * @param data Implementation specific data that contains information and arguments to & about the oracle.
     *
     * @return success If no valid (recent) rate is available, returns false else true.
     * @return rate The rate of the requested asset / pair / pool.
     */
    function peek(
        bytes calldata data
    ) external view returns (bool success, uint256 rate);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

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

/**
 * @title IJigsawUSD
 * @dev Interface for the Jigsaw Stablecoin Contract.
 */
interface IJigsawUSD is IERC20 {
    /**
     * @notice event emitted when the mint limit is updated
     */
    event MintLimitUpdated(uint256 oldLimit, uint256 newLimit);

    /**
     * @notice Contract that contains all the necessary configs of the protocol.
     * @return The manager contract.
     */
    function manager() external view returns (IManager);

    /**
     * @notice Returns the max mint limit.
     */
    function mintLimit() external view returns (uint256);

    /**
     * @notice Sets the maximum mintable amount.
     *
     * @notice Requirements:
     * - Must be called by the contract owner.
     *
     * @notice Effects:
     * - Updates the `mintLimit` state variable.
     *
     * @notice Emits:
     * - `MintLimitUpdated` event indicating mint limit update operation.
     * @param _limit The new mint limit.
     */
    function updateMintLimit(
        uint256 _limit
    ) external;

    /**
     * @notice Mints tokens.
     *
     * @notice Requirements:
     * - Must be called by the Stables Manager Contract
     *  .
     * @notice Effects:
     * - Mints the specified amount of tokens to the given address.
     *
     * @param _to Address of the user receiving minted tokens.
     * @param _amount The amount to be minted.
     */
    function mint(address _to, uint256 _amount) external;

    /**
     * @notice Burns tokens from the `msg.sender`.
     *
     * @notice Requirements:
     * - Must be called by the token holder.
     *
     * @notice Effects:
     * - Burns the specified amount of tokens from the caller's balance.
     *
     * @param _amount The amount of tokens to be burnt.
     */
    function burn(
        uint256 _amount
    ) external;

    /**
     * @notice Burns tokens from an address.
     *
     * - Must be called by the Stables Manager Contract
     *
     * @notice Effects: Burns the specified amount of tokens from the specified address.
     *
     * @param _user The user to burn it from.
     * @param _amount The amount of tokens to be burnt.
     */
    function burnFrom(address _user, uint256 _amount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

// Receipt token interface
interface IReceiptToken is IERC20, IERC20Metadata, IERC20Errors {
    // -- Events --

    /**
     * @notice Emitted when the minter address is updated
     *  @param oldMinter The address of the old minter
     *  @param newMinter The address of the new minter
     */
    event MinterUpdated(address oldMinter, address newMinter);

    // --- Initialization ---

    /**
     * @notice This function initializes the contract (instead of a constructor) to be cloned.
     *
     * @notice Requirements:
     * - The contract must not be already initialized.
     * - The `__minter` must not be the zero address.
     *
     * @notice Effects:
     * - Sets `_initialized` to true.
     * - Updates `_name`, `_symbol`, `minter` state variables.
     * - Stores `__owner` as owner.
     *
     * @param __name Receipt token name.
     * @param __symbol Receipt token symbol.
     * @param __minter Receipt token minter.
     * @param __owner Receipt token owner.
     */
    function initialize(string memory __name, string memory __symbol, address __minter, address __owner) external;

    /**
     * @notice Mints receipt tokens.
     *
     * @notice Requirements:
     * - Must be called by the Minter or Owner of the Contract.
     *
     * @notice Effects:
     * - Mints the specified amount of tokens to the given address.
     *
     * @param _user Address of the user receiving minted tokens.
     * @param _amount The amount to be minted.
     */
    function mint(address _user, uint256 _amount) external;

    /**
     * @notice Burns tokens from an address.
     *
     * @notice Requirements:
     * - Must be called by the Minter or Owner of the Contract.
     *
     * @notice Effects:
     * - Burns the specified amount of tokens from the specified address.
     *
     * @param _user The user to burn it from.
     * @param _amount The amount of tokens to be burnt.
     */
    function burnFrom(address _user, uint256 _amount) external;

    /**
     * @notice Sets minter.
     *
     * @notice Requirements:
     * - Must be called by the Minter or Owner of the Contract.
     * - The `_minter` must be different from `minter`.
     *
     * @notice Effects:
     * - Updates minter state variable.
     *
     * @notice Emits:
     * - `MinterUpdated` event indicating minter update operation.
     *
     * @param _minter The user to burn it from.
     */
    function setMinter(
        address _minter
    ) external;
}

File 25 of 26 : IStrategyManagerMin.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IStrategyManagerMin {
    /**
     * @notice Returns the strategy info.
     */
    function strategyInfo(
        address _strategy
    ) external view returns (uint256, bool, bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/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/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@pyth/=lib/pyth-sdk-solidity/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "pyth-sdk-solidity/=lib/pyth-sdk-solidity/",
    "v3-core/=lib/v3-core/contracts/",
    "v3-periphery/=lib/v3-periphery/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"address","name":"_manager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","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":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holding","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bool","name":"add","type":"bool"}],"name":"CollateralAdjusted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holding","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenOutResult","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenInResult","type":"uint256"}],"name":"Invested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holding","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"strategyFrom","type":"address"},{"indexed":false,"internalType":"address","name":"strategyTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenOutResult","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenInResult","type":"uint256"}],"name":"InvestmentMoved","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":"token","type":"address"},{"indexed":true,"internalType":"address","name":"holding","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holding","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"withdrawnAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initialInvestment","type":"uint256"},{"indexed":false,"internalType":"int256","name":"yield","type":"int256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"StrategyClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"bool","name":"active","type":"bool"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"StrategyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_holding","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"claimInvestment","outputs":[{"internalType":"uint256","name":"withdrawnAmount","type":"uint256"},{"internalType":"uint256","name":"initialInvestment","type":"uint256"},{"internalType":"int256","name":"yield","type":"int256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"claimRewards","outputs":[{"internalType":"uint256[]","name":"rewards","type":"uint256[]"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_holding","type":"address"}],"name":"getHoldingToStrategy","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_holding","type":"address"}],"name":"getHoldingToStrategyLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_minSharesAmountOut","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"invest","outputs":[{"internalType":"uint256","name":"tokenOutAmount","type":"uint256"},{"internalType":"uint256","name":"tokenInAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"contract IManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"components":[{"internalType":"address","name":"strategyFrom","type":"address"},{"internalType":"address","name":"strategyTo","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"bytes","name":"dataFrom","type":"bytes"},{"internalType":"bytes","name":"dataTo","type":"bytes"},{"internalType":"uint256","name":"strategyToMinSharesAmountOut","type":"uint256"}],"internalType":"struct IStrategyManager.MoveInvestmentData","name":"_data","type":"tuple"}],"name":"moveInvestment","outputs":[{"internalType":"uint256","name":"tokenOutAmount","type":"uint256"},{"internalType":"uint256","name":"tokenInAmount","type":"uint256"}],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"strategy","type":"address"}],"name":"strategyInfo","outputs":[{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"whitelisted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"components":[{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"bool","name":"whitelisted","type":"bool"}],"internalType":"struct IStrategyManager.StrategyInfo","name":"_info","type":"tuple"}],"name":"updateStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405234801561000f575f80fd5b50604051613d83380380613d8383398101604081905261002e9161014d565b816001600160a01b03811661005d57604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610066816100c7565b5060016002556003805460ff191690556001600160a01b0381166100b55760405162461bcd60e51b8152600401610054906020808252600490820152633330363560e01b604082015260600190565b6001600160a01b03166080525061017e565b600180546001600160a01b03191690556100e0816100e3565b50565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114610148575f80fd5b919050565b5f806040838503121561015e575f80fd5b61016783610132565b915061017560208401610132565b90509250929050565b608051613bc36101c05f395f81816101da015281816108090152818161141701528181611a8f015281816120940152818161262d0152612c7a0152613bc35ff3fe608060405234801561000f575f80fd5b506004361061012f575f3560e01c8063715018a6116100ad578063a01951ac1161007d578063b7565cec11610063578063b7565cec146102d3578063e30c397814610322578063f2fde38b14610340575f80fd5b8063a01951ac1461029f578063ae2c5faa146102b2575f80fd5b8063715018a61461026a57806379ba5097146102725780638456cb591461027a5780638da5cb5b14610282575f80fd5b80633edd2f3511610102578063481c6a75116100e8578063481c6a75146101d55780634ebfd1ec146102215780635c975abb14610254575f80fd5b80633edd2f35146101ad5780633f4ba83a146101cd575f80fd5b806318b4d37f14610133578063223e54791461015d5780633d92397d146101725780633dc270731461019a575b5f80fd5b610146610141366004613212565b610353565b6040516101549291906132b3565b60405180910390f35b61017061016b36600461330a565b6106c4565b005b610185610180366004613325565b61095e565b60408051928352602083019190915201610154565b6101706101a8366004613378565b6110ec565b6101c06101bb36600461330a565b61130d565b60405161015491906133d4565b610170611343565b6101fc7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610154565b61023461022f3660046133e6565b61135d565b604080519485526020850193909352918301526060820152608001610154565b60035460ff166040519015158152602001610154565b61017061184d565b6101706118b1565b610170611928565b5f5473ffffffffffffffffffffffffffffffffffffffff166101fc565b6101856102ad366004613466565b611940565b6102c56102c036600461330a565b611f37565b604051908152602001610154565b6103056102e136600461330a565b60046020525f90815260409020805460019091015460ff8082169161010090041683565b604080519384529115156020840152151590820152606001610154565b60015473ffffffffffffffffffffffffffffffffffffffff166101fc565b61017061034e36600461330a565b611f64565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526004602052604090206001015460609081908590610100900460ff166103fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b60405180910390fd5b610403612013565b61040b612054565b5f610414612091565b6040517f6681141100000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9190911690636681141190602401602060405180830381865afa15801561047e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a291906134bf565b90506104ac612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152919091169063e3933ca690602401602060405180830381865afa158015610518573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061053c91906134e7565b6105a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b6040517f18b4d37f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8816906318b4d37f906105fa9084908a908a90600401613549565b5f604051808303815f875af1158015610615573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261065a9190810190613693565b90945092505f5b84518110156106af576106a784828151811061067f5761067f613751565b602002602001015186838151811061069957610699613751565b602002602001015184612124565b600101610661565b50506106bb6001600255565b50935093915050565b6106cc6122e3565b8073ffffffffffffffffffffffffffffffffffffffff811661074c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303000000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260046020526040902060010154610100900460ff16156107e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330313400000000000000000000000000000000000000000000000000000000604082015260600190565b5f60405180606001604052805f81526020015f151581526020015f151581525090507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663877887826040518163ffffffff1660e01b8152600401602060405180830381865afa158015610870573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610894919061377e565b815260016020808301828152604080850184815273ffffffffffffffffffffffffffffffffffffffff88165f818152600490955282852087518155935193909501805491517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009092169315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16939093176101009115159190910217909155517f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f19190a2505050565b5f8061096d602084018461330a565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040902060010154610100900460ff16610a05576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b610a15604085016020860161330a565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040902060010154610100900460ff16610aad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b610ab5612013565b610abd612054565b5f610ac6612091565b6040517f6681141100000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9190911690636681141190602401602060405180830381865afa158015610b30573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b5491906134bf565b9050610b5e612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152919091169063e3933ca690602401602060405180830381865afa158015610bca573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bee91906134e7565b610c56576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b610c66604087016020880161330a565b73ffffffffffffffffffffffffffffffffffffffff16610c89602088018861330a565b73ffffffffffffffffffffffffffffffffffffffff1603610d08576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330383600000000000000000000000000000000000000000000000000000000604082015260600190565b60045f610d1b6040890160208a0161330a565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040015f206001015460ff16610daf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3132303200000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff8716610dd3602088018861330a565b73ffffffffffffffffffffffffffffffffffffffff16636daf390b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e1b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e3f91906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614610ebe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303100000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff8716610ee5604088016020890161330a565b73ffffffffffffffffffffffffffffffffffffffff16636daf390b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f5191906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614610fd0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330383500000000000000000000000000000000000000000000000000000000604082015260600190565b5f610ffa8289610fe360208b018b61330a565b60408b0135610ff560608d018d613795565b612335565b505050905061102f8289896020016020810190611017919061330a565b8460a08c013561102a60808e018e613795565b6128d4565b909650945073ffffffffffffffffffffffffffffffffffffffff80891690339084167f86c9b90f154d6b4cb9ff6711084368466fc19b7322c7ae82b7ddafd1ddd2615b61107f60208c018c61330a565b61108f60408d0160208e0161330a565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152808d013590820152606081018b9052608081018a905260a00160405180910390a450506110e36001600255565b50509250929050565b6110f46122e3565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600101548290610100900460ff1661118e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b61119e60608301604084016137f6565b611206576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303400000000000000000000000000000000000000000000000000000000604082015260600190565b61271082351115611275576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303500000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260046020526040902082906112a58282613811565b505073ffffffffffffffffffffffffffffffffffffffff83167fc9ec6b1142e515c9baec16b0d3807cd0b96ff98ae056a66115f3f36baf7f782a6112ef60408501602086016137f6565b604080519115158252853560208301520160405180910390a2505050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260056020526040902060609061133d90612b16565b92915050565b61134b6122e3565b611353612b29565b61135b612b65565b565b73ffffffffffffffffffffffffffffffffffffffff84165f908152600460205260408120600101548190819081908890610100900460ff166113fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b8a3373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16631ef3a04c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561147e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114a291906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614806115705750336114c8612091565b6040517f6a7942e300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529190911690636a7942e390602401602060405180830381865afa158015611534573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061155891906134bf565b73ffffffffffffffffffffffffffffffffffffffff16145b6115d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3130303000000000000000000000000000000000000000000000000000000000604082015260600190565b885f8111611644576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3230303100000000000000000000000000000000000000000000000000000000604082015260600190565b61164c612013565b611654612054565b61165c612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8f81166004830152919091169063e3933ca690602401602060405180830381865afa1580156116c8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116ec91906134e7565b611754576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b6117628d8d8d8d8d8d612335565b809750819850829950839a50505050508b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167fb047cb0a95cc7b31a486835621bbfdff0c883d07e844efa9055d31612f5c2a548e8e8c8c8c8c60405161182b9695949392919073ffffffffffffffffffffffffffffffffffffffff969096168652602086019490945260408501929092526060840152608083015260a082015260c00190565b60405180910390a461183d6001600255565b5050509650965096509692505050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3130303000000000000000000000000000000000000000000000000000000000604082015260600190565b600154339073ffffffffffffffffffffffffffffffffffffffff16811461191c576040517f118cdaa700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016103f2565b61192581612be2565b50565b6119306122e3565b611938612054565b61135b612c13565b73ffffffffffffffffffffffffffffffffffffffff85165f9081526004602052604081206001015481908790610100900460ff166119dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b865f8111611a48576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3230303100000000000000000000000000000000000000000000000000000000604082015260600190565b6040517fb5af090f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808c1660048301528b917f00000000000000000000000000000000000000000000000000000000000000009091169063b5af090f90602401602060405180830381865afa158015611ad6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611afa91906134e7565b611b62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303100000000000000000000000000000000000000000000000000000000604082015260600190565b611b6a612054565b611b72612013565b5f611b7b612091565b6040517f6681141100000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9190911690636681141190602401602060405180830381865afa158015611be5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c0991906134bf565b9050611c13612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152919091169063e3933ca690602401602060405180830381865afa158015611c7f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ca391906134e7565b611d0b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff8b165f9081526004602052604090206001015460ff16611d9e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3132303200000000000000000000000000000000000000000000000000000000604082015260600190565b8b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff16636daf390b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dfe573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2291906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614611ea1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330383500000000000000000000000000000000000000000000000000000000604082015260600190565b611eb0818d8d8d8d8d8d6128d4565b6040805173ffffffffffffffffffffffffffffffffffffffff8f81168252602082018f905291810184905260608101839052929850909650808e169133918416907f593122f12448add14ff7e822f5e3a54bb9e43f528591bfe5f748fcc61bb920b09060800160405180910390a450611f296001600255565b505050965096945050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260056020526040812061133d90612c6e565b611f6c6122e3565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155611fce5f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b600280540361204e576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028055565b60035460ff161561135b576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166319aeb94b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061211f91906134bf565b905090565b81156122de575f80612134612c77565b6040517fe7c8294a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152919091169063e7c8294a906024016040805180830381865afa15801561219f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c39190613876565b909250905073ffffffffffffffffffffffffffffffffffffffff8116158015906121ea5750815b156122db57604080518581526001602082015273ffffffffffffffffffffffffffffffffffffffff80881692908616917f03959069557261fc759a9eaca561f7c7112bdb2a14e9155b212dc8fe089e458f910160405180910390a361224d612c77565b6040517f5978103400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015287811660248301526044820187905291909116906359781034906064015f604051808303815f87803b1580156122c4575f80fd5b505af11580156122d6573d5f803e3d5ffd5b505050505b50505b505050565b5f5473ffffffffffffffffffffffffffffffffffffffff16331461135b576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103f2565b5f805f805f6040518060c001604052808a73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f81526020015f81526020015f81526020015f815250905061238d815f0151898d612ce1565b80516040517fa7b7e98800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063a7b7e988906123ea908b908f908f908d908d906004016138a3565b6080604051808303815f875af1158015612406573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061242a91906138ff565b608085015260608401526040830152602082018190526124a8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330313600000000000000000000000000000000000000000000000000000000604082015260600190565b5f8160600151131561254e576124bc612c77565b60608201516040517f5978103400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8e811660048301528d8116602483015260448201929092529116906359781034906064015f604051808303815f87803b158015612537575f80fd5b505af1158015612549573d5f803e3d5ffd5b505050505b5f8160600151121561261457612562612c77565b73ffffffffffffffffffffffffffffffffffffffff1663cce19f818c8c61258c8560600151612f59565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff938416600482015292909116602483015260448201526064015f604051808303815f87803b1580156125fd575f80fd5b505af115801561260f573d5f803e3d5ffd5b505050505b3373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16631ef3a04c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612694573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126b891906134bf565b73ffffffffffffffffffffffffffffffffffffffff16146127dc576126db612c77565b6040517f3966a28b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c811660048301528d811660248301529190911690633966a28b90604401602060405180830381865afa15801561274f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061277391906134e7565b156127dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303300000000000000000000000000000000000000000000000000000000604082015260600190565b80516040517feb82031200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d811660048301529091169063eb820312906024016040805180830381865afa158015612848573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286c9190613932565b60a083018190525f0390506128ab5773ffffffffffffffffffffffffffffffffffffffff8b165f9081526005602052604090206128a9908a612f6e565b505b602081015160408201516060830151608090930151919d909c50919a5098509650505050505050565b5f808673ffffffffffffffffffffffffffffffffffffffff16635143a9fe89888c88886040518663ffffffff1660e01b8152600401612917959493929190613954565b60408051808303815f875af1158015612932573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129569190613932565b9092509050811580159061296a5750848210155b6129d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330333000000000000000000000000000000000000000000000000000000000604082015260600190565b6129da612c77565b6040517f3966a28b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301528b811660248301529190911690633966a28b90604401602060405180830381865afa158015612a4e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a7291906134e7565b15612adb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303300000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff89165f908152600560205260409020612b099088612f8f565b5097509795505050505050565b60605f612b2283612fb0565b9392505050565b60035460ff1661135b576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b6d612b29565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b600180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905561192581613009565b612c1b612054565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612bb83390565b5f61133d825490565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f0370a266040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fb573d5f803e3d5ffd5b5f8373ffffffffffffffffffffffffffffffffffffffff1663149aa9166040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d2b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d4f919061377e565b6040517feb82031200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529192505f9186169063eb820312906024016040805180830381865afa158015612dbd573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612de19190613932565b9150505f818511612df25784612df4565b815b90506012831115612e2657612e0a6012846139d2565b612e1590600a613b06565b612e1f9082613b11565b9050612e49565b612e318360126139d2565b612e3c90600a613b06565b612e469082613b49565b90505b808673ffffffffffffffffffffffffffffffffffffffff166374dc573b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e93573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eb791906134bf565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015291909116906370a0823190602401602060405180830381865afa158015612f23573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f47919061377e565b1015612f51575f80fd5b505050505050565b5f80821215612f6a57815f0361133d565b5090565b5f612b228373ffffffffffffffffffffffffffffffffffffffff841661307d565b5f612b228373ffffffffffffffffffffffffffffffffffffffff8416613160565b6060815f01805480602002602001604051908101604052809291908181526020018280548015612ffd57602002820191905f5260205f20905b815481526020019060010190808311612fe9575b50505050509050919050565b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f8181526001830160205260408120548015613157575f61309f6001836139d2565b85549091505f906130b2906001906139d2565b9050808214613111575f865f0182815481106130d0576130d0613751565b905f5260205f200154905080875f0184815481106130f0576130f0613751565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061312257613122613b60565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061133d565b5f91505061133d565b5f8181526001830160205260408120546131a557508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561133d565b505f61133d565b73ffffffffffffffffffffffffffffffffffffffff81168114611925575f80fd5b5f8083601f8401126131dd575f80fd5b50813567ffffffffffffffff8111156131f4575f80fd5b60208301915083602082850101111561320b575f80fd5b9250929050565b5f805f60408486031215613224575f80fd5b833561322f816131ac565b9250602084013567ffffffffffffffff81111561324a575f80fd5b613256868287016131cd565b9497909650939450505050565b5f8151808452602084019350602083015f5b828110156132a957815173ffffffffffffffffffffffffffffffffffffffff16865260209586019590910190600101613275565b5093949350505050565b604080825283519082018190525f9060208501906060840190835b818110156132ec5783518352602093840193909201916001016132ce565b505083810360208501526133008186613263565b9695505050505050565b5f6020828403121561331a575f80fd5b8135612b22816131ac565b5f8060408385031215613336575f80fd5b8235613341816131ac565b9150602083013567ffffffffffffffff81111561335c575f80fd5b830160c0818603121561336d575f80fd5b809150509250929050565b5f80828403608081121561338a575f80fd5b8335613395816131ac565b925060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0820112156133c6575f80fd5b506020830190509250929050565b602081525f612b226020830184613263565b5f805f805f8060a087890312156133fb575f80fd5b8635613406816131ac565b95506020870135613416816131ac565b94506040870135613426816131ac565b935060608701359250608087013567ffffffffffffffff811115613448575f80fd5b61345489828a016131cd565b979a9699509497509295939492505050565b5f805f805f8060a0878903121561347b575f80fd5b8635613486816131ac565b95506020870135613496816131ac565b94506040870135935060608701359250608087013567ffffffffffffffff811115613448575f80fd5b5f602082840312156134cf575f80fd5b8151612b22816131ac565b8015158114611925575f80fd5b5f602082840312156134f7575f80fd5b8151612b22816134da565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff84168152604060208201525f613578604083018486613502565b95945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156135f5576135f5613581565b604052919050565b5f67ffffffffffffffff82111561361657613616613581565b5060051b60200190565b5f82601f83011261362f575f80fd5b815161364261363d826135fd565b6135ae565b8082825260208201915060208360051b860101925085831115613663575f80fd5b602085015b8381101561368957805161367b816131ac565b835260209283019201613668565b5095945050505050565b5f80604083850312156136a4575f80fd5b825167ffffffffffffffff8111156136ba575f80fd5b8301601f810185136136ca575f80fd5b80516136d861363d826135fd565b8082825260208201915060208360051b8501019250878311156136f9575f80fd5b6020840193505b8284101561371b578351825260209384019390910190613700565b80955050505050602083015167ffffffffffffffff81111561373b575f80fd5b61374785828601613620565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6020828403121561378e575f80fd5b5051919050565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126137c8575f80fd5b83018035915067ffffffffffffffff8211156137e2575f80fd5b60200191503681900382131561320b575f80fd5b5f60208284031215613806575f80fd5b8135612b22816134da565b81358155600181016020830135613827816134da565b81546040850135613837816134da565b61ff0081151560081b1660ff841515167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000841617178455505050505050565b5f8060408385031215613887575f80fd5b8251613892816134da565b602084015190925061336d816131ac565b85815273ffffffffffffffffffffffffffffffffffffffff8516602082015273ffffffffffffffffffffffffffffffffffffffff84166040820152608060608201525f6138f4608083018486613502565b979650505050505050565b5f805f8060808587031215613912575f80fd5b505082516020840151604085015160609095015191969095509092509050565b5f8060408385031215613943575f80fd5b505080516020909101519092909150565b73ffffffffffffffffffffffffffffffffffffffff8616815284602082015273ffffffffffffffffffffffffffffffffffffffff84166040820152608060608201525f6138f4608083018486613502565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181038181111561133d5761133d6139a5565b6001815b6001841115613a2057808504811115613a0457613a046139a5565b6001841615613a1257908102905b60019390931c9280026139e9565b935093915050565b5f82613a365750600161133d565b81613a4257505f61133d565b8160018114613a585760028114613a6257613a7e565b600191505061133d565b60ff841115613a7357613a736139a5565b50506001821b61133d565b5060208310610133831016604e8410600b8410161715613aa1575081810a61133d565b613acc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84846139e5565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115613afe57613afe6139a5565b029392505050565b5f612b228383613a28565b5f82613b44577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b808202811582820484141761133d5761133d6139a5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea2646970667358221220ae108c7444bedf5d55932d4b594e6ceee78bcec7b085514a46d0e18f83e7a3a964736f6c634300081a00330000000000000000000000004fc32e984d689063e9284750d94a4eee6ba5c24c0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b05228

Deployed Bytecode

0x608060405234801561000f575f80fd5b506004361061012f575f3560e01c8063715018a6116100ad578063a01951ac1161007d578063b7565cec11610063578063b7565cec146102d3578063e30c397814610322578063f2fde38b14610340575f80fd5b8063a01951ac1461029f578063ae2c5faa146102b2575f80fd5b8063715018a61461026a57806379ba5097146102725780638456cb591461027a5780638da5cb5b14610282575f80fd5b80633edd2f3511610102578063481c6a75116100e8578063481c6a75146101d55780634ebfd1ec146102215780635c975abb14610254575f80fd5b80633edd2f35146101ad5780633f4ba83a146101cd575f80fd5b806318b4d37f14610133578063223e54791461015d5780633d92397d146101725780633dc270731461019a575b5f80fd5b610146610141366004613212565b610353565b6040516101549291906132b3565b60405180910390f35b61017061016b36600461330a565b6106c4565b005b610185610180366004613325565b61095e565b60408051928352602083019190915201610154565b6101706101a8366004613378565b6110ec565b6101c06101bb36600461330a565b61130d565b60405161015491906133d4565b610170611343565b6101fc7f0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b0522881565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610154565b61023461022f3660046133e6565b61135d565b604080519485526020850193909352918301526060820152608001610154565b60035460ff166040519015158152602001610154565b61017061184d565b6101706118b1565b610170611928565b5f5473ffffffffffffffffffffffffffffffffffffffff166101fc565b6101856102ad366004613466565b611940565b6102c56102c036600461330a565b611f37565b604051908152602001610154565b6103056102e136600461330a565b60046020525f90815260409020805460019091015460ff8082169161010090041683565b604080519384529115156020840152151590820152606001610154565b60015473ffffffffffffffffffffffffffffffffffffffff166101fc565b61017061034e36600461330a565b611f64565b73ffffffffffffffffffffffffffffffffffffffff83165f9081526004602052604090206001015460609081908590610100900460ff166103fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b60405180910390fd5b610403612013565b61040b612054565b5f610414612091565b6040517f6681141100000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9190911690636681141190602401602060405180830381865afa15801561047e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104a291906134bf565b90506104ac612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152919091169063e3933ca690602401602060405180830381865afa158015610518573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061053c91906134e7565b6105a4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b6040517f18b4d37f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8816906318b4d37f906105fa9084908a908a90600401613549565b5f604051808303815f875af1158015610615573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261065a9190810190613693565b90945092505f5b84518110156106af576106a784828151811061067f5761067f613751565b602002602001015186838151811061069957610699613751565b602002602001015184612124565b600101610661565b50506106bb6001600255565b50935093915050565b6106cc6122e3565b8073ffffffffffffffffffffffffffffffffffffffff811661074c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303000000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff82165f90815260046020526040902060010154610100900460ff16156107e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330313400000000000000000000000000000000000000000000000000000000604082015260600190565b5f60405180606001604052805f81526020015f151581526020015f151581525090507f0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b0522873ffffffffffffffffffffffffffffffffffffffff1663877887826040518163ffffffff1660e01b8152600401602060405180830381865afa158015610870573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610894919061377e565b815260016020808301828152604080850184815273ffffffffffffffffffffffffffffffffffffffff88165f818152600490955282852087518155935193909501805491517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009092169315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16939093176101009115159190910217909155517f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f19190a2505050565b5f8061096d602084018461330a565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040902060010154610100900460ff16610a05576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b610a15604085016020860161330a565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260046020526040902060010154610100900460ff16610aad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b610ab5612013565b610abd612054565b5f610ac6612091565b6040517f6681141100000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9190911690636681141190602401602060405180830381865afa158015610b30573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b5491906134bf565b9050610b5e612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152919091169063e3933ca690602401602060405180830381865afa158015610bca573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bee91906134e7565b610c56576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b610c66604087016020880161330a565b73ffffffffffffffffffffffffffffffffffffffff16610c89602088018861330a565b73ffffffffffffffffffffffffffffffffffffffff1603610d08576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330383600000000000000000000000000000000000000000000000000000000604082015260600190565b60045f610d1b6040890160208a0161330a565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040015f206001015460ff16610daf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3132303200000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff8716610dd3602088018861330a565b73ffffffffffffffffffffffffffffffffffffffff16636daf390b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e1b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e3f91906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614610ebe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303100000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff8716610ee5604088016020890161330a565b73ffffffffffffffffffffffffffffffffffffffff16636daf390b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f5191906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614610fd0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330383500000000000000000000000000000000000000000000000000000000604082015260600190565b5f610ffa8289610fe360208b018b61330a565b60408b0135610ff560608d018d613795565b612335565b505050905061102f8289896020016020810190611017919061330a565b8460a08c013561102a60808e018e613795565b6128d4565b909650945073ffffffffffffffffffffffffffffffffffffffff80891690339084167f86c9b90f154d6b4cb9ff6711084368466fc19b7322c7ae82b7ddafd1ddd2615b61107f60208c018c61330a565b61108f60408d0160208e0161330a565b6040805173ffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152808d013590820152606081018b9052608081018a905260a00160405180910390a450506110e36001600255565b50509250929050565b6110f46122e3565b73ffffffffffffffffffffffffffffffffffffffff82165f908152600460205260409020600101548290610100900460ff1661118e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b61119e60608301604084016137f6565b611206576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303400000000000000000000000000000000000000000000000000000000604082015260600190565b61271082351115611275576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303500000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260046020526040902082906112a58282613811565b505073ffffffffffffffffffffffffffffffffffffffff83167fc9ec6b1142e515c9baec16b0d3807cd0b96ff98ae056a66115f3f36baf7f782a6112ef60408501602086016137f6565b604080519115158252853560208301520160405180910390a2505050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260056020526040902060609061133d90612b16565b92915050565b61134b6122e3565b611353612b29565b61135b612b65565b565b73ffffffffffffffffffffffffffffffffffffffff84165f908152600460205260408120600101548190819081908890610100900460ff166113fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b8a3373ffffffffffffffffffffffffffffffffffffffff167f0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b0522873ffffffffffffffffffffffffffffffffffffffff16631ef3a04c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561147e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114a291906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614806115705750336114c8612091565b6040517f6a7942e300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529190911690636a7942e390602401602060405180830381865afa158015611534573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061155891906134bf565b73ffffffffffffffffffffffffffffffffffffffff16145b6115d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3130303000000000000000000000000000000000000000000000000000000000604082015260600190565b885f8111611644576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3230303100000000000000000000000000000000000000000000000000000000604082015260600190565b61164c612013565b611654612054565b61165c612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8f81166004830152919091169063e3933ca690602401602060405180830381865afa1580156116c8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116ec91906134e7565b611754576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b6117628d8d8d8d8d8d612335565b809750819850829950839a50505050508b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff168e73ffffffffffffffffffffffffffffffffffffffff167fb047cb0a95cc7b31a486835621bbfdff0c883d07e844efa9055d31612f5c2a548e8e8c8c8c8c60405161182b9695949392919073ffffffffffffffffffffffffffffffffffffffff969096168652602086019490945260408501929092526060840152608083015260a082015260c00190565b60405180910390a461183d6001600255565b5050509650965096509692505050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3130303000000000000000000000000000000000000000000000000000000000604082015260600190565b600154339073ffffffffffffffffffffffffffffffffffffffff16811461191c576040517f118cdaa700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016103f2565b61192581612be2565b50565b6119306122e3565b611938612054565b61135b612c13565b73ffffffffffffffffffffffffffffffffffffffff85165f9081526004602052604081206001015481908790610100900460ff166119dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330323900000000000000000000000000000000000000000000000000000000604082015260600190565b865f8111611a48576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3230303100000000000000000000000000000000000000000000000000000000604082015260600190565b6040517fb5af090f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff808c1660048301528b917f0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b052289091169063b5af090f90602401602060405180830381865afa158015611ad6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611afa91906134e7565b611b62576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303100000000000000000000000000000000000000000000000000000000604082015260600190565b611b6a612054565b611b72612013565b5f611b7b612091565b6040517f6681141100000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff9190911690636681141190602401602060405180830381865afa158015611be5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c0991906134bf565b9050611c13612091565b6040517fe3933ca600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152919091169063e3933ca690602401602060405180830381865afa158015611c7f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ca391906134e7565b611d0b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330303200000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff8b165f9081526004602052604090206001015460ff16611d9e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3132303200000000000000000000000000000000000000000000000000000000604082015260600190565b8b73ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff16636daf390b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dfe573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e2291906134bf565b73ffffffffffffffffffffffffffffffffffffffff1614611ea1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330383500000000000000000000000000000000000000000000000000000000604082015260600190565b611eb0818d8d8d8d8d8d6128d4565b6040805173ffffffffffffffffffffffffffffffffffffffff8f81168252602082018f905291810184905260608101839052929850909650808e169133918416907f593122f12448add14ff7e822f5e3a54bb9e43f528591bfe5f748fcc61bb920b09060800160405180910390a450611f296001600255565b505050965096945050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f90815260056020526040812061133d90612c6e565b611f6c6122e3565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155611fce5f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b600280540361204e576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028055565b60035460ff161561135b576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b0522873ffffffffffffffffffffffffffffffffffffffff166319aeb94b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fb573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061211f91906134bf565b905090565b81156122de575f80612134612c77565b6040517fe7c8294a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152919091169063e7c8294a906024016040805180830381865afa15801561219f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c39190613876565b909250905073ffffffffffffffffffffffffffffffffffffffff8116158015906121ea5750815b156122db57604080518581526001602082015273ffffffffffffffffffffffffffffffffffffffff80881692908616917f03959069557261fc759a9eaca561f7c7112bdb2a14e9155b212dc8fe089e458f910160405180910390a361224d612c77565b6040517f5978103400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015287811660248301526044820187905291909116906359781034906064015f604051808303815f87803b1580156122c4575f80fd5b505af11580156122d6573d5f803e3d5ffd5b505050505b50505b505050565b5f5473ffffffffffffffffffffffffffffffffffffffff16331461135b576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103f2565b5f805f805f6040518060c001604052808a73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f81526020015f81526020015f81526020015f815250905061238d815f0151898d612ce1565b80516040517fa7b7e98800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063a7b7e988906123ea908b908f908f908d908d906004016138a3565b6080604051808303815f875af1158015612406573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061242a91906138ff565b608085015260608401526040830152602082018190526124a8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330313600000000000000000000000000000000000000000000000000000000604082015260600190565b5f8160600151131561254e576124bc612c77565b60608201516040517f5978103400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8e811660048301528d8116602483015260448201929092529116906359781034906064015f604051808303815f87803b158015612537575f80fd5b505af1158015612549573d5f803e3d5ffd5b505050505b5f8160600151121561261457612562612c77565b73ffffffffffffffffffffffffffffffffffffffff1663cce19f818c8c61258c8560600151612f59565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff938416600482015292909116602483015260448201526064015f604051808303815f87803b1580156125fd575f80fd5b505af115801561260f573d5f803e3d5ffd5b505050505b3373ffffffffffffffffffffffffffffffffffffffff167f0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b0522873ffffffffffffffffffffffffffffffffffffffff16631ef3a04c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612694573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906126b891906134bf565b73ffffffffffffffffffffffffffffffffffffffff16146127dc576126db612c77565b6040517f3966a28b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c811660048301528d811660248301529190911690633966a28b90604401602060405180830381865afa15801561274f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061277391906134e7565b156127dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303300000000000000000000000000000000000000000000000000000000604082015260600190565b80516040517feb82031200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d811660048301529091169063eb820312906024016040805180830381865afa158015612848573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286c9190613932565b60a083018190525f0390506128ab5773ffffffffffffffffffffffffffffffffffffffff8b165f9081526005602052604090206128a9908a612f6e565b505b602081015160408201516060830151608090930151919d909c50919a5098509650505050505050565b5f808673ffffffffffffffffffffffffffffffffffffffff16635143a9fe89888c88886040518663ffffffff1660e01b8152600401612917959493929190613954565b60408051808303815f875af1158015612932573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129569190613932565b9092509050811580159061296a5750848210155b6129d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3330333000000000000000000000000000000000000000000000000000000000604082015260600190565b6129da612c77565b6040517f3966a28b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301528b811660248301529190911690633966a28b90604401602060405180830381865afa158015612a4e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a7291906134e7565b15612adb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f29060208082526004908201527f3331303300000000000000000000000000000000000000000000000000000000604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff89165f908152600560205260409020612b099088612f8f565b5097509795505050505050565b60605f612b2283612fb0565b9392505050565b60035460ff1661135b576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612b6d612b29565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b600180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905561192581613009565b612c1b612054565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612bb83390565b5f61133d825490565b5f7f0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b0522873ffffffffffffffffffffffffffffffffffffffff1663f0370a266040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120fb573d5f803e3d5ffd5b5f8373ffffffffffffffffffffffffffffffffffffffff1663149aa9166040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d2b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d4f919061377e565b6040517feb82031200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529192505f9186169063eb820312906024016040805180830381865afa158015612dbd573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612de19190613932565b9150505f818511612df25784612df4565b815b90506012831115612e2657612e0a6012846139d2565b612e1590600a613b06565b612e1f9082613b11565b9050612e49565b612e318360126139d2565b612e3c90600a613b06565b612e469082613b49565b90505b808673ffffffffffffffffffffffffffffffffffffffff166374dc573b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e93573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eb791906134bf565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015291909116906370a0823190602401602060405180830381865afa158015612f23573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f47919061377e565b1015612f51575f80fd5b505050505050565b5f80821215612f6a57815f0361133d565b5090565b5f612b228373ffffffffffffffffffffffffffffffffffffffff841661307d565b5f612b228373ffffffffffffffffffffffffffffffffffffffff8416613160565b6060815f01805480602002602001604051908101604052809291908181526020018280548015612ffd57602002820191905f5260205f20905b815481526020019060010190808311612fe9575b50505050509050919050565b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f8181526001830160205260408120548015613157575f61309f6001836139d2565b85549091505f906130b2906001906139d2565b9050808214613111575f865f0182815481106130d0576130d0613751565b905f5260205f200154905080875f0184815481106130f0576130f0613751565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061312257613122613b60565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061133d565b5f91505061133d565b5f8181526001830160205260408120546131a557508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561133d565b505f61133d565b73ffffffffffffffffffffffffffffffffffffffff81168114611925575f80fd5b5f8083601f8401126131dd575f80fd5b50813567ffffffffffffffff8111156131f4575f80fd5b60208301915083602082850101111561320b575f80fd5b9250929050565b5f805f60408486031215613224575f80fd5b833561322f816131ac565b9250602084013567ffffffffffffffff81111561324a575f80fd5b613256868287016131cd565b9497909650939450505050565b5f8151808452602084019350602083015f5b828110156132a957815173ffffffffffffffffffffffffffffffffffffffff16865260209586019590910190600101613275565b5093949350505050565b604080825283519082018190525f9060208501906060840190835b818110156132ec5783518352602093840193909201916001016132ce565b505083810360208501526133008186613263565b9695505050505050565b5f6020828403121561331a575f80fd5b8135612b22816131ac565b5f8060408385031215613336575f80fd5b8235613341816131ac565b9150602083013567ffffffffffffffff81111561335c575f80fd5b830160c0818603121561336d575f80fd5b809150509250929050565b5f80828403608081121561338a575f80fd5b8335613395816131ac565b925060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0820112156133c6575f80fd5b506020830190509250929050565b602081525f612b226020830184613263565b5f805f805f8060a087890312156133fb575f80fd5b8635613406816131ac565b95506020870135613416816131ac565b94506040870135613426816131ac565b935060608701359250608087013567ffffffffffffffff811115613448575f80fd5b61345489828a016131cd565b979a9699509497509295939492505050565b5f805f805f8060a0878903121561347b575f80fd5b8635613486816131ac565b95506020870135613496816131ac565b94506040870135935060608701359250608087013567ffffffffffffffff811115613448575f80fd5b5f602082840312156134cf575f80fd5b8151612b22816131ac565b8015158114611925575f80fd5b5f602082840312156134f7575f80fd5b8151612b22816134da565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff84168152604060208201525f613578604083018486613502565b95945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156135f5576135f5613581565b604052919050565b5f67ffffffffffffffff82111561361657613616613581565b5060051b60200190565b5f82601f83011261362f575f80fd5b815161364261363d826135fd565b6135ae565b8082825260208201915060208360051b860101925085831115613663575f80fd5b602085015b8381101561368957805161367b816131ac565b835260209283019201613668565b5095945050505050565b5f80604083850312156136a4575f80fd5b825167ffffffffffffffff8111156136ba575f80fd5b8301601f810185136136ca575f80fd5b80516136d861363d826135fd565b8082825260208201915060208360051b8501019250878311156136f9575f80fd5b6020840193505b8284101561371b578351825260209384019390910190613700565b80955050505050602083015167ffffffffffffffff81111561373b575f80fd5b61374785828601613620565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6020828403121561378e575f80fd5b5051919050565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126137c8575f80fd5b83018035915067ffffffffffffffff8211156137e2575f80fd5b60200191503681900382131561320b575f80fd5b5f60208284031215613806575f80fd5b8135612b22816134da565b81358155600181016020830135613827816134da565b81546040850135613837816134da565b61ff0081151560081b1660ff841515167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000841617178455505050505050565b5f8060408385031215613887575f80fd5b8251613892816134da565b602084015190925061336d816131ac565b85815273ffffffffffffffffffffffffffffffffffffffff8516602082015273ffffffffffffffffffffffffffffffffffffffff84166040820152608060608201525f6138f4608083018486613502565b979650505050505050565b5f805f8060808587031215613912575f80fd5b505082516020840151604085015160609095015191969095509092509050565b5f8060408385031215613943575f80fd5b505080516020909101519092909150565b73ffffffffffffffffffffffffffffffffffffffff8616815284602082015273ffffffffffffffffffffffffffffffffffffffff84166040820152608060608201525f6138f4608083018486613502565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181038181111561133d5761133d6139a5565b6001815b6001841115613a2057808504811115613a0457613a046139a5565b6001841615613a1257908102905b60019390931c9280026139e9565b935093915050565b5f82613a365750600161133d565b81613a4257505f61133d565b8160018114613a585760028114613a6257613a7e565b600191505061133d565b60ff841115613a7357613a736139a5565b50506001821b61133d565b5060208310610133831016604e8410600b8410161715613aa1575081810a61133d565b613acc7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84846139e5565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115613afe57613afe6139a5565b029392505050565b5f612b228383613a28565b5f82613b44577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b808202811582820484141761133d5761133d6139a5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea2646970667358221220ae108c7444bedf5d55932d4b594e6ceee78bcec7b085514a46d0e18f83e7a3a964736f6c634300081a0033

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

0000000000000000000000004fc32e984d689063e9284750d94a4eee6ba5c24c0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b05228

-----Decoded View---------------
Arg [0] : _initialOwner (address): 0x4FC32e984D689063e9284750D94A4EeE6bA5c24c
Arg [1] : _manager (address): 0x0000000E44A948Ab0c83F2C65D3a2C4A06B05228

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000004fc32e984d689063e9284750d94a4eee6ba5c24c
Arg [1] : 0000000000000000000000000000000e44a948ab0c83f2c65d3a2c4a06b05228


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.