Contract 0xCB75dd494b0205aC57e19B14d1ab5AE599637D86

 

Contract Overview

Balance:
0 ETH

ETH Value:
$0.00

Token:
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x468412ca11707812f31195b3de58b05e9315202a980df4f4382f56bfb5ab32770x60c06040110221142022-05-02 10:13:47217 days 11 hrs agoJones DAO: Deployer IN  Create: JonesSSOVCallV3Strategy0 ETH0.049225400268 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x864b48b89ee529f96c0eeb3ee2fc1c9a121b27fa719196bdb2340e4a07a7cbbb170595712022-07-05 14:45:38153 days 6 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0x6c69f8462641969d643ed222f62c7321a80c36838523f6d63e4da78cbafe0a0a156531602022-06-24 15:30:05164 days 5 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0x5ba98ad75ab87eb90ffc2b680bcfc6b9030e12460 ETH
0x6c69f8462641969d643ed222f62c7321a80c36838523f6d63e4da78cbafe0a0a156531602022-06-24 15:30:05164 days 5 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86Dopex: DPX Token0 ETH
0x6c69f8462641969d643ed222f62c7321a80c36838523f6d63e4da78cbafe0a0a156531602022-06-24 15:30:05164 days 5 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0xd82fbaa5433e7b01bbad8d1d3f3c983515e65310baf3287cc8df89f5a51bd706156529332022-06-24 15:27:38164 days 5 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0xdb62c01c23e247cea558e90814dbeb215b92c81f0 ETH
0xd82fbaa5433e7b01bbad8d1d3f3c983515e65310baf3287cc8df89f5a51bd706156529332022-06-24 15:27:38164 days 5 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0x2c347270811b63d5f9832ee43728ff6fefade7e658cf30ac3b5f4d164b1beccf135271962022-06-02 13:35:37186 days 7 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0x5ba98ad75ab87eb90ffc2b680bcfc6b9030e12460 ETH
0x2c347270811b63d5f9832ee43728ff6fefade7e658cf30ac3b5f4d164b1beccf135271962022-06-02 13:35:37186 days 7 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86Dopex: DPX Token0 ETH
0x2c347270811b63d5f9832ee43728ff6fefade7e658cf30ac3b5f4d164b1beccf135271962022-06-02 13:35:37186 days 7 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0xca3c66a067f88ef6827306f66ef16ee8792eae96fdc753ceed7126d2c041b481135251542022-06-02 13:12:52186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0xdb62c01c23e247cea558e90814dbeb215b92c81f0 ETH
0xca3c66a067f88ef6827306f66ef16ee8792eae96fdc753ceed7126d2c041b481135251542022-06-02 13:12:52186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0xdb62c01c23e247cea558e90814dbeb215b92c81f0 ETH
0xca3c66a067f88ef6827306f66ef16ee8792eae96fdc753ceed7126d2c041b481135251542022-06-02 13:12:52186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0xdb62c01c23e247cea558e90814dbeb215b92c81f0 ETH
0xca3c66a067f88ef6827306f66ef16ee8792eae96fdc753ceed7126d2c041b481135251542022-06-02 13:12:52186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0x7487c19df56b7ea764fc269468b5d3014565052f0 ETH
0xca3c66a067f88ef6827306f66ef16ee8792eae96fdc753ceed7126d2c041b481135251542022-06-02 13:12:52186 days 8 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0x801f84c631102b5430c00120c7faad71b12e66c5ab6c6b667be79158b37b90b2135248932022-06-02 13:09:52186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0xdb62c01c23e247cea558e90814dbeb215b92c81f0 ETH
0x801f84c631102b5430c00120c7faad71b12e66c5ab6c6b667be79158b37b90b2135248932022-06-02 13:09:52186 days 8 hrs ago 0xdb62c01c23e247cea558e90814dbeb215b92c81f 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0x801f84c631102b5430c00120c7faad71b12e66c5ab6c6b667be79158b37b90b2135248932022-06-02 13:09:52186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0xdb62c01c23e247cea558e90814dbeb215b92c81f0 ETH
0x801f84c631102b5430c00120c7faad71b12e66c5ab6c6b667be79158b37b90b2135248932022-06-02 13:09:52186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0x7487c19df56b7ea764fc269468b5d3014565052f0 ETH
0x801f84c631102b5430c00120c7faad71b12e66c5ab6c6b667be79158b37b90b2135248932022-06-02 13:09:52186 days 8 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0xf13b6580edc39c7f2e58cbdca1cc07fa0cdbb7ec6dc263ab09d59787ed97fd6c135236662022-06-02 12:53:07186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0x7487c19df56b7ea764fc269468b5d3014565052f0 ETH
0xf13b6580edc39c7f2e58cbdca1cc07fa0cdbb7ec6dc263ab09d59787ed97fd6c135236662022-06-02 12:53:07186 days 8 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0x4018ad35b684f0a7b676d78f1c47ea36f97b11b02d99a0ea5fdbc27df3506a28135229832022-06-02 12:42:53186 days 8 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0x5ba98ad75ab87eb90ffc2b680bcfc6b9030e12460 ETH
0x4018ad35b684f0a7b676d78f1c47ea36f97b11b02d99a0ea5fdbc27df3506a28135229832022-06-02 12:42:53186 days 8 hrs ago Jones DAO: Asset Management Multisig 0xcb75dd494b0205ac57e19b14d1ab5ae599637d860 ETH
0x7bfeafbe3da05a8ad56052fa92db288c47f38edcf1d6e27a0cd252cfb513b5e4130686052022-05-27 14:27:06192 days 6 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86 0x5ba98ad75ab87eb90ffc2b680bcfc6b9030e12460 ETH
0x7bfeafbe3da05a8ad56052fa92db288c47f38edcf1d6e27a0cd252cfb513b5e4130686052022-05-27 14:27:06192 days 6 hrs ago 0xcb75dd494b0205ac57e19b14d1ab5ae599637d86Dopex: DPX Token0 ETH
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
JonesSSOVCallV3Strategy

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 23 : JonesSSOVCallV3Strategy.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;

/**
 * Libraries
 */
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {SushiRouterWrapper} from "../VaultsV2/library/SushiRouterWrapper.sol";

/**
 * Interfaces
 */
import {IUniswapV2Router02} from "../interfaces/IUniswapV2Router02.sol";
import {JonesSSOVV3StrategyBase} from "./JonesSSOVV3StrategyBase.sol";
import {ISsovV3} from "../interfaces/ISsovV3.sol";

contract JonesSSOVCallV3Strategy is JonesSSOVV3StrategyBase {
    using SafeERC20 for IERC20;
    using SushiRouterWrapper for IUniswapV2Router02;

    /**
     * @dev Initialize the Strategy.
     * @param _name of Stategy.
     * @param _asset Base Asset of Strategy.
     * @param _SSOV Dopex SSOV contract the Strategy will interact with.
     * @param _viewer Dopex Viewer contract.
     * @param _router Dopex Router contract.
     * @param _governor of Strategy.
     */
    constructor(
        bytes32 _name,
        address _asset,
        address _SSOV,
        address _viewer,
        address _router,
        address _governor
    )
        JonesSSOVV3StrategyBase(
            _name,
            _asset,
            _SSOV,
            _viewer,
            _router,
            _governor
        )
    {
        // Token spending approval for SSOV
        IERC20(asset).safeApprove(address(SSOV), type(uint256).max);

        // Token spending approval for SSOV router
        IERC20(asset).safeApprove(address(router), type(uint256).max);
    }

    /**
     * @notice Used to convert any reward tokens received by the Strategy back to Base Asset.
     */
    function sellRewardTokensForBaseToken(
        address[] memory _rewardTokens,
        uint256[] memory _minBaseOutputAmounts
    ) public onlyRole(KEEPER) {
        address[][] memory routes = new address[][](_rewardTokens.length);
        for (uint256 i = 0; i < _rewardTokens.length; i++) {
            IERC20 rewardToken = IERC20(_rewardTokens[i]);

            if (asset == wETH) {
                routes[i] = new address[](2);
                routes[i][0] = address(rewardToken);
                routes[i][1] = wETH;
            } else {
                routes[i] = new address[](3);
                routes[i][0] = address(rewardToken);
                routes[i][1] = wETH;
                routes[i][2] = asset;
            }

            IERC20(rewardToken).safeApprove(
                address(sushiRouter),
                IERC20(rewardToken).balanceOf(address(this))
            );
        }

        sushiRouter.sellTokens(
            _minBaseOutputAmounts,
            _rewardTokens,
            address(this),
            routes
        );
    }

    function updateSSOVAddress(ISsovV3 _newSSOV) public onlyRole(GOVERNOR) {
        // revoke old
        IERC20(asset).safeApprove(address(SSOV), 0);

        // set new ssov
        SSOV = _newSSOV;

        // approve new
        IERC20(asset).safeApprove(address(SSOV), type(uint256).max);
    }
}

File 2 of 23 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 3 of 23 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../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;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @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, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 4 of 23 : SushiRouterWrapper.sol
// SPDX-License-Identifier: GPL-3.0
/*                            ******@@@@@@@@@**@*                               
                        ***@@@@@@@@@@@@@@@@@@@@@@**                             
                     *@@@@@@**@@@@@@@@@@@@@@@@@*@@@*                            
                  *@@@@@@@@@@@@@@@@@@@*@@@@@@@@@@@*@**                          
                 *@@@@@@@@@@@@@@@@@@*@@@@@@@@@@@@@@@@@*                         
                **@@@@@@@@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@**                       
                **@@@@@@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@@@@@*                      
                **@@@@@@@@@@@@@@@@*************************                    
                **@@@@@@@@***********************************                   
                 *@@@***********************&@@@@@@@@@@@@@@@****,    ******@@@@*
           *********************@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@************* 
      ***@@@@@@@@@@@@@@@*****@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@****@@*********      
   **@@@@@**********************@@@@*****************#@@@@**********            
  *@@******************************************************                     
 *@************************************                                         
 @*******************************                                               
 *@*************************                                                    
   ********************* 
   
    /$$$$$                                               /$$$$$$$   /$$$$$$   /$$$$$$ 
   |__  $$                                              | $$__  $$ /$$__  $$ /$$__  $$
      | $$  /$$$$$$  /$$$$$$$   /$$$$$$   /$$$$$$$      | $$  \ $$| $$  \ $$| $$  \ $$
      | $$ /$$__  $$| $$__  $$ /$$__  $$ /$$_____/      | $$  | $$| $$$$$$$$| $$  | $$
 /$$  | $$| $$  \ $$| $$  \ $$| $$$$$$$$|  $$$$$$       | $$  | $$| $$__  $$| $$  | $$
| $$  | $$| $$  | $$| $$  | $$| $$_____/ \____  $$      | $$  | $$| $$  | $$| $$  | $$
|  $$$$$$/|  $$$$$$/| $$  | $$|  $$$$$$$ /$$$$$$$/      | $$$$$$$/| $$  | $$|  $$$$$$/
 \______/  \______/ |__/  |__/ \_______/|_______/       |_______/ |__/  |__/ \______/                                      
*/

pragma solidity ^0.8.2;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IUniswapV2Router02} from "../../interfaces/IUniswapV2Router02.sol";

library SushiRouterWrapper {
    using SafeERC20 for IERC20;

    /**
     * Sells the received tokens for the provided amounts for the last token in the route
     * Temporary solution until we implement accumulation policy.
     * @param self the sushi router used to perform the sale.
     * @param _assetAmounts output amount from selling the tokens.
     * @param _tokens tokens to sell.
     * @param _recepient recepient address.
     * @param _routes routes to sell each token
     */
    function sellTokens(
        IUniswapV2Router02 self,
        uint256[] memory _assetAmounts,
        address[] memory _tokens,
        address _recepient,
        address[][] memory _routes
    ) public {
        uint256 amountsLength = _assetAmounts.length;
        uint256 tokensLength = _tokens.length;
        uint256 routesLength = _routes.length;

        require(amountsLength == tokensLength, "SRE1");
        require(routesLength == tokensLength, "SRE1");

        uint256 deadline = block.timestamp + 120;
        for (uint256 i = 0; i < tokensLength; i++) {
            _sellTokens(
                self,
                IERC20(_tokens[i]),
                _assetAmounts[i],
                _recepient,
                deadline,
                _routes[i]
            );
        }
    }

    /**
     * Sells the received tokens for the provided amounts for ETH
     * Temporary solution until we implement accumulation policy.
     * @param self the sushi router used to perform the sale.
     * @param _assetAmounts output amount from selling the tokens.
     * @param _tokens tokens to sell.
     * @param _recepient recepient address.
     * @param _routes routes to sell each token.
     */
    function sellTokensForEth(
        IUniswapV2Router02 self,
        uint256[] memory _assetAmounts,
        address[] memory _tokens,
        address _recepient,
        address[][] memory _routes
    ) public {
        uint256 amountsLength = _assetAmounts.length;
        uint256 tokensLength = _tokens.length;
        uint256 routesLength = _routes.length;

        require(amountsLength == tokensLength, "SRE1");
        require(routesLength == tokensLength, "SRE1");

        uint256 deadline = block.timestamp + 120;
        for (uint256 i = 0; i < tokensLength; i++) {
            _sellTokensForEth(
                self,
                IERC20(_tokens[i]),
                _assetAmounts[i],
                _recepient,
                deadline,
                _routes[i]
            );
        }
    }

    /**
     * Sells one token for a given amount of another.
     * @param self the Sushi router used to perform the sale.
     * @param _route route to swap the token.
     * @param _assetAmount output amount of the last token in the route from selling the first.
     * @param _recepient recepient address.
     */
    function sellTokensForExactTokens(
        IUniswapV2Router02 self,
        address[] memory _route,
        uint256 _assetAmount,
        address _recepient,
        address _token
    ) public {
        require(_route.length >= 2, "SRE2");
        uint256 balance = IERC20(_route[0]).balanceOf(_recepient);
        if (balance > 0) {
            uint256 deadline = block.timestamp + 120; // Two minutes
            _sellTokens(
                self,
                IERC20(_token),
                _assetAmount,
                _recepient,
                deadline,
                _route
            );
        }
    }

    function _sellTokensForEth(
        IUniswapV2Router02 _sushiRouter,
        IERC20 _token,
        uint256 _assetAmount,
        address _recepient,
        uint256 _deadline,
        address[] memory _route
    ) private {
        uint256 balance = _token.balanceOf(_recepient);
        if (balance > 0) {
            _sushiRouter.swapExactTokensForETH(
                balance,
                _assetAmount,
                _route,
                _recepient,
                _deadline
            );
        }
    }

    function _sellTokens(
        IUniswapV2Router02 _sushiRouter,
        IERC20 _token,
        uint256 _assetAmount,
        address _recepient,
        uint256 _deadline,
        address[] memory _route
    ) private {
        uint256 balance = _token.balanceOf(_recepient);
        if (balance > 0) {
            _sushiRouter.swapExactTokensForTokens(
                balance,
                _assetAmount,
                _route,
                _recepient,
                _deadline
            );
        }
    }

    // ERROR MAPPING:
    // {
    //   "SRE1": "Rewards: token, amount and routes lenght must match",
    //   "SRE2": "Length of route must be at least 2",
    // }
}

File 5 of 23 : IUniswapV2Router02.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.2;

import "./IUniswapV2Router01.sol";

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountETH);

    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

File 6 of 23 : JonesSSOVV3StrategyBase.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;

/**
 * Libraries
 */
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {SsovV3Wrapper} from "./library/SsovV3Wrapper.sol";

/**
 * Interfaces
 */
import {ISsovV3} from "../interfaces/ISsovV3.sol";
import {ISsovV3Viewer} from "../interfaces/ISsovV3Viewer.sol";
import {IwETH} from "../interfaces/IwETH.sol";
import {IUniswapV2Router02} from "../interfaces/IUniswapV2Router02.sol";
import {ISsovV3Router} from "../interfaces/ISsovV3Router.sol";
import {JonesStrategyV3Base} from "./JonesStrategyV3Base.sol";
import {IStrategy} from "./IStrategy.sol";

contract JonesSSOVV3StrategyBase is JonesStrategyV3Base {
    using SafeERC20 for IERC20;
    using SsovV3Wrapper for ISsovV3;

    IUniswapV2Router02 internal constant sushiRouter =
        IUniswapV2Router02(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506);

    address public constant wETH = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;

    /// SSOV contract
    ISsovV3 public SSOV;

    /// SSOV Viewer contract
    ISsovV3Viewer public SSOVViewer;

    /// SSOV viewer
    ISsovV3Viewer public viewer;

    /// ssov v3 router
    ISsovV3Router public router;

    constructor(
        bytes32 _name,
        address _asset,
        address _SSOV,
        address _viewer,
        address _router,
        address _governor
    ) JonesStrategyV3Base(_name, _asset, _governor) {
        if (_SSOV == address(0)) {
            revert ADDRESS_CANNOT_BE_ZERO_ADDRESS();
        }

        if (_viewer == address(0)) {
            revert ADDRESS_CANNOT_BE_ZERO_ADDRESS();
        }

        if (_router == address(0)) {
            revert ADDRESS_CANNOT_BE_ZERO_ADDRESS();
        }

        SSOV = ISsovV3(_SSOV);
        viewer = ISsovV3Viewer(_viewer);
        router = ISsovV3Router(_router);
    }

    // ============================= Mutative functions ================================

    /**
     * Deposits funds to SSOV at desired strike price.
     * @param _strikeIndex Strike price index.
     * @param _amount Amount of collateral to deposit.
     * @return tokenId Token id of the deposit.
     */
    function depositSSOV(uint256 _strikeIndex, uint256 _amount)
        public
        onlyRole(KEEPER)
        returns (uint256 tokenId)
    {
        return SSOV.depositSSOV(_strikeIndex, _amount, address(this));
    }

    /**
     * Deposits funds to SSOV at multiple desired strike prices.
     * @param _strikeIndices Strike price indices.
     * @param _amounts Amounts of assets to deposit.
     * Returns a bool to indicate if the deposits went through successfully.
     */
    function depositSSOVMultiple(
        uint256[] memory _strikeIndices,
        uint256[] memory _amounts
    ) public onlyRole(KEEPER) returns (bool) {
        router.multideposit(_strikeIndices, _amounts, address(this), SSOV);
        return true;
    }

    /**
     * Buys options from Dopex SSOV.
     * @param _strikeIndex Strike index for current epoch.
     * @param _amount Amount of puts/calls to purchase.
     * Returns bool to indicate if put/call purchase went through sucessfully.
     */
    function purchaseOption(uint256 _strikeIndex, uint256 _amount)
        public
        onlyRole(KEEPER)
        returns (bool)
    {
        return SSOV.purchaseOption(_strikeIndex, _amount, address(this));
    }

    /**
     * @notice Settles the SSOV epoch.
     * @param _ssovEpoch The SSOV epoch to settle.
     * @param _ssovStrikes The SSOV strike indexes to settle.
     */
    function settleEpoch(uint256 _ssovEpoch, uint256[] memory _ssovStrikes)
        public
        onlyRole(KEEPER)
        returns (bool)
    {
        SSOV.settleEpoch(viewer, address(this), _ssovEpoch, _ssovStrikes);
        return true;
    }

    function withdrawTokenId(uint256 _tokenId)
        public
        onlyRole(KEEPER)
        returns (bool)
    {
        SSOV.withdraw(_tokenId, address(this));
        return true;
    }

    /**
     * @notice Withdraws from SSOV for the given `_epoch` and `_strikes`.
     * @param _epoch The SSOV epoch to withdraw from.
     * @param _strikes The SSOV strikes.
     */
    function withdrawEpoch(uint256 _epoch, uint256[] memory _strikes)
        public
        onlyRole(KEEPER)
        returns (bool)
    {
        SSOV.withdrawEpoch(viewer, _epoch, _strikes, address(this));
        return true;
    }

    // ============================= Management Functions ================================

    /**
     * @inheritdoc IStrategy
     */
    function migrateFunds(
        address _to,
        address[] memory _tokens,
        bool _shouldTransferEth,
        bool _shouldTransferERC721
    ) public virtual override onlyRole(GOVERNOR) {
        // transfer tokens
        for (uint256 i = 0; i < _tokens.length; i++) {
            IERC20 token = IERC20(_tokens[i]);
            uint256 assetBalance = token.balanceOf(address(this));
            if (assetBalance > 0) {
                token.safeTransfer(_to, assetBalance);
            }
            if (address(token) == asset) {
                totalDeposited = 0;
            }
        }

        // migrate ETH balance
        uint256 balanceGwei = address(this).balance;
        if (balanceGwei > 0 && _shouldTransferEth) {
            payable(_to).transfer(balanceGwei);
        }

        // withdraw erc721 tokens
        if (_shouldTransferERC721) {
            uint256[] memory depositTokens = viewer.walletOfOwner(
                address(this),
                SSOV
            );
            for (uint256 i = 0; i < depositTokens.length; i++) {
                uint256 tokenId = depositTokens[i];
                SSOV.safeTransferFrom(address(this), _to, tokenId);
            }
        }

        emit FundsMigrated(_to);
    }

    // ============================= ERC721 ================================
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external pure returns (bytes4) {
        return
            bytes4(
                keccak256("onERC721Received(address,address,uint256,bytes)")
            );
    }
}

File 7 of 23 : ISsovV3.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface ISsovV3 is IERC721 {
    struct Addresses {
        address feeStrategy;
        address stakingStrategy;
        address optionPricing;
        address priceOracle;
        address volatilityOracle;
        address feeDistributor;
        address optionsTokenImplementation;
    }

    struct EpochData {
        uint256 startTime;
        uint256 expiry;
        uint256 settlementPrice;
        uint256 totalCollateralBalance; // Premium + Deposits from all strikes
        uint256 collateralExchangeRate; // Exchange rate for collateral to underlying
        uint256[] totalRewardsCollected;
        uint256[] rewardDistributionRatios;
        address[] rewardTokensToDistribute;
        uint256[] strikes;
        bool expired;
    }

    struct EpochStrikeData {
        /// Address of the strike token
        address strikeToken;
        /// Last checkpoint for the vault for an epoch for a strike
        VaultCheckpoint lastVaultCheckpoint;
        uint256[] rewardsStoredForPremiums;
        uint256[] rewardsDistributionRatiosForPremiums;
    }

    struct VaultCheckpoint {
        uint256 premiumCollectedCumulative;
        uint256 activeCollateral;
        uint256 totalCollateral;
        uint256 activeCollateralRatio;
        uint256 premiumDistributionRatio;
        uint256[] rewardDistributionRatios;
    }

    struct WritePosition {
        uint256 epoch;
        uint256 strike;
        uint256 collateralAmount;
        VaultCheckpoint vaultCheckpoint;
    }

    function expire() external;

    function deposit(
        uint256 strikeIndex,
        uint256 amount,
        address user
    ) external returns (uint256 tokenId);

    function purchase(
        uint256 strikeIndex,
        uint256 amount,
        address user
    ) external returns (uint256 premium, uint256 totalFee);

    function settle(
        uint256 strikeIndex,
        uint256 amount,
        uint256 epoch
    ) external returns (uint256 pnl);

    function withdraw(uint256 tokenId, address to)
        external
        returns (
            uint256 collateralTokenWithdrawAmount,
            uint256[] memory rewardTokenWithdrawAmounts
        );

    function getUnderlyingPrice() external returns (uint256);

    function getCollateralPrice() external returns (uint256);

    function getVolatility(uint256 _strike) external view returns (uint256);

    function calculatePremium(
        uint256 _strike,
        uint256 _amount,
        uint256 _expiry
    ) external view returns (uint256 premium);

    function calculatePnl(
        uint256 price,
        uint256 strike,
        uint256 amount
    ) external pure returns (uint256);

    function calculatePurchaseFees(uint256 strike, uint256 amount)
        external
        returns (uint256);

    function calculateSettlementFees(
        uint256 settlementPrice,
        uint256 pnl,
        uint256 amount
    ) external view returns (uint256);

    function getEpochTimes(uint256 epoch)
        external
        view
        returns (uint256 start, uint256 end);

    function getEpochStrikes(uint256 epoch)
        external
        view
        returns (uint256[] memory);

    function writePosition(uint256 tokenId)
        external
        view
        returns (
            uint256 epoch,
            uint256 strike,
            uint256 collateralAmount,
            VaultCheckpoint memory vaultCheckpoint
        );

    function getEpochStrikeTokens(uint256 epoch)
        external
        view
        returns (address[] memory);

    function getLastVaultCheckpoint(uint256 epoch, uint256 strike)
        external
        view
        returns (VaultCheckpoint memory);

    function underlyingSymbol() external returns (string memory);

    function isPut() external view returns (bool);

    function addresses() external view returns (Addresses memory);

    function collateralToken() external view returns (IERC20);

    function currentEpoch() external returns (uint256);

    function expireDelayTolerance() external returns (uint256);

    function collateralPrecision() external returns (uint256);

    function getEpochData(uint256 epoch)
        external
        view
        returns (EpochData memory);

    function epochStrikeData(uint256 epoch, uint256 strike)
        external
        view
        returns (EpochStrikeData memory);

    // Dopex management only
    function expire(uint256 _settlementPrice) external;

    function bootstrap(uint256[] memory strikes, uint256 expiry) external;

    function addToContractWhitelist(address _contract) external;
}

File 8 of 23 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 9 of 23 : IUniswapV2Router01.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);

    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );

    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 liquidity
        );

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityETH(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountToken, uint256 amountETH);

    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountA, uint256 amountB);

    function removeLiquidityETHWithPermit(
        address token,
        uint256 liquidity,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline,
        bool approveMax,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 amountToken, uint256 amountETH);

    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactETHForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapTokensForExactETH(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapExactTokensForETH(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function swapETHForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) external pure returns (uint256 amountB);

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountOut);

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountIn);

    function getAmountsOut(uint256 amountIn, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function getAmountsIn(uint256 amountOut, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);
}

File 10 of 23 : SsovV3Wrapper.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ISsovV3} from "../../interfaces/ISsovV3.sol";
import {ISsovV3Viewer} from "../../interfaces/ISsovV3Viewer.sol";
import {ISsovV3Router} from "../../interfaces/ISsovV3Router.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

library SsovV3Wrapper {
    using SafeERC20 for IERC20;

    /**
     * Deposits funds to SSOV at desired strike price.
     * @param _strikeIndex Strike price index.
     * @param _amount Amount of Collateral to deposit.
     * @param _depositor The depositor contract
     * @return tokenId tokenId of the deposit.
     */
    function depositSSOV(
        ISsovV3 self,
        uint256 _strikeIndex,
        uint256 _amount,
        address _depositor
    ) public returns (uint256 tokenId) {
        tokenId = self.deposit(_strikeIndex, _amount, _depositor);
        uint256 epoch = self.currentEpoch();
        emit SSOVDeposit(epoch, _strikeIndex, _amount, tokenId);
    }

    /**
     * Purchase Dopex option.
     * @param self Dopex SSOV contract.
     * @param _strikeIndex Strike index for current epoch.
     * @param _amount Amount of options to purchase.
     * @param _buyer Jones strategy contract.
     * @return Whether deposit was successful.
     */
    function purchaseOption(
        ISsovV3 self,
        uint256 _strikeIndex,
        uint256 _amount,
        address _buyer
    ) public returns (bool) {
        (uint256 premium, uint256 totalFee) = self.purchase(
            _strikeIndex,
            _amount,
            _buyer
        );

        emit SSOVPurchase(
            self.currentEpoch(),
            _strikeIndex,
            _amount,
            premium,
            totalFee,
            address(self.collateralToken())
        );

        return true;
    }

    function _settleEpoch(
        ISsovV3 self,
        uint256 _epoch,
        IERC20 _strikeToken,
        address _caller,
        uint256 _strikePrice,
        uint256 _settlementPrice,
        uint256 _strikeIndex
    ) private {
        uint256 strikeTokenBalance = _strikeToken.balanceOf(_caller);
        uint256 pnl = self.calculatePnl(
            _settlementPrice,
            _strikePrice,
            strikeTokenBalance
        );
        if (strikeTokenBalance > 0 && pnl > 0) {
            _strikeToken.safeApprove(address(self), strikeTokenBalance);
            self.settle(_strikeIndex, strikeTokenBalance, _epoch);
        }
    }

    /**
     * Settles options from Dopex SSOV at the end of an epoch.
     * @param _caller the address settling the epoch
     * @param _epoch the epoch to settle
     * @param _strikes the strikes to settle
     * Returns bool to indicate if epoch settlement was successful.
     */
    function settleEpoch(
        ISsovV3 self,
        ISsovV3Viewer _viewer,
        address _caller,
        uint256 _epoch,
        uint256[] memory _strikes
    ) public returns (bool) {
        if (_strikes.length == 0) {
            return false;
        }

        ISsovV3.EpochData memory epochData = self.getEpochData(_epoch);
        uint256[] memory epochStrikes = epochData.strikes;
        uint256 price = epochData.settlementPrice;

        address[] memory strikeTokens = _viewer.getEpochStrikeTokens(
            _epoch,
            self
        );
        for (uint256 i = 0; i < _strikes.length; i++) {
            uint256 index = _strikes[i];
            IERC20 strikeToken = IERC20(strikeTokens[index]);
            uint256 strikePrice = epochStrikes[index];
            _settleEpoch(
                self,
                _epoch,
                strikeToken,
                _caller,
                strikePrice,
                price,
                index
            );
        }
        return true;
    }

    /**
     * Allows withdraw of all erc721 tokens ssov deposit for the given epoch and strikes.
     */
    function withdrawEpoch(
        ISsovV3 self,
        ISsovV3Viewer _viewer,
        uint256 _epoch,
        uint256[] memory _strikes,
        address _caller
    ) public {
        uint256[] memory tokenIds = _viewer.walletOfOwner(_caller, self);
        for (uint256 i = 0; i < tokenIds.length; i++) {
            (uint256 epoch, uint256 strike, , ) = self.writePosition(
                tokenIds[i]
            );
            if (epoch == _epoch) {
                for (uint256 j = 0; j < _strikes.length; j++) {
                    if (strike == _strikes[j]) {
                        self.withdraw(tokenIds[i], _caller);
                    }
                }
            }
        }
    }

    /**
     * Emitted when new Deposit to SSOV is made
     * @param _epoch SSOV epoch (indexed)
     * @param _strikeIndex SSOV strike index
     * @param _amount deposited Collateral Token amount
     * @param _tokenId token ID of the deposit
     */
    event SSOVDeposit(
        uint256 indexed _epoch,
        uint256 _strikeIndex,
        uint256 _amount,
        uint256 _tokenId
    );

    /**
     * emitted when new put/call from SSOV is purchased
     * @param _epoch SSOV epoch (indexed)
     * @param _strikeIndex SSOV strike index
     * @param _amount put amount
     * @param _premium put/call premium
     * @param _totalFee put/call total fee
     */
    event SSOVPurchase(
        uint256 indexed _epoch,
        uint256 _strikeIndex,
        uint256 _amount,
        uint256 _premium,
        uint256 _totalFee,
        address _token
    );
}

File 11 of 23 : ISsovV3Viewer.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

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

interface ISsovV3Viewer {
    function getEpochStrikeTokens(uint256 epoch, ISsovV3 ssov)
        external
        view
        returns (address[] memory strikeTokens);

    function walletOfOwner(address owner, ISsovV3 ssov)
        external
        view
        returns (uint256[] memory tokenIds);
}

File 12 of 23 : IwETH.sol
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.2;

interface IwETH {
    function deposit() external payable;

    function withdraw(uint256 amount) external;

    function transfer(address to, uint256 value) external returns (bool);

    function approve(address spender, uint256 value) external returns (bool);

    function balanceOf(address owner) external view returns (uint256);
}

File 13 of 23 : ISsovV3Router.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

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

interface ISsovV3Router {
    function multideposit(
        uint256[] calldata _strikeIndices,
        uint256[] calldata _amounts,
        address _to,
        ISsovV3 _ssov
    ) external;
}

File 14 of 23 : JonesStrategyV3Base.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IStrategy} from "./IStrategy.sol";
import {IVault} from "./IVault.sol";

abstract contract JonesStrategyV3Base is IStrategy, AccessControl {
    using SafeERC20 for IERC20;

    address internal _vault;
    bytes32 public constant KEEPER = keccak256("KEEPER");
    bytes32 public constant GOVERNOR = keccak256("GOVERNOR");
    address public immutable asset;
    bytes32 public immutable name;
    uint256 public totalDeposited;
    bool public isVaultSet;

    /**
     * @dev Sets the values for {name} and {asset}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(
        bytes32 _name,
        address _asset,
        address _governor
    ) {
        if (_asset == address(0)) {
            revert ADDRESS_CANNOT_BE_ZERO_ADDRESS();
        }

        if (_governor == address(0)) {
            revert ADDRESS_CANNOT_BE_ZERO_ADDRESS();
        }

        name = _name;
        asset = _asset;

        _grantRole(GOVERNOR, _governor);
        _grantRole(KEEPER, _governor);
    }

    // ============================= View functions ================================

    /**
     * @inheritdoc IStrategy
     */
    function getVault() public view virtual returns (address) {
        if (!isVaultSet) {
            revert VAULT_NOT_ATTACHED();
        }
        return address(_vault);
    }

    /**
     * @inheritdoc IStrategy
     */
    function getUnused() public view virtual override returns (uint256) {
        return IERC20(asset).balanceOf(address(this));
    }

    // ============================= Mutative functions ================================

    function grantKeeperRole(address _to) public onlyRole(GOVERNOR) {
        if (_to == address(0)) {
            revert ADDRESS_CANNOT_BE_ZERO_ADDRESS();
        }
        _grantRole(KEEPER, _to);
    }

    function revokeKeeperRole(address _from) public onlyRole(GOVERNOR) {
        _revokeRole(KEEPER, _from);
    }

    /**
     * @inheritdoc IStrategy
     */
    function setVault(address _newVault) public virtual onlyRole(GOVERNOR) {
        if (isVaultSet) {
            revert VAULT_ALREADY_ATTACHED();
        }

        if (_newVault == address(0)) {
            revert ADDRESS_CANNOT_BE_ZERO_ADDRESS();
        }

        _vault = _newVault;
        IERC20(asset).safeApprove(_vault, type(uint256).max);
        isVaultSet = true;
        emit VaultSet(_msgSender(), _vault);
    }

    /**
     * @inheritdoc IStrategy
     */
    function detach() public virtual override onlyRole(GOVERNOR) {
        if (!isVaultSet) {
            revert VAULT_NOT_ATTACHED();
        }
        _repay();
        if (getUnused() > 0) {
            revert STRATEGY_STILL_HAS_ASSET_BALANCE();
        }
        address prevVault = _vault;
        IERC20(asset).safeApprove(_vault, 0);
        _vault = address(0);
        isVaultSet = false;
        emit VaultDetached(msg.sender, prevVault);
    }

    /**
     * @inheritdoc IStrategy
     */
    function borrow(uint256 _amount) public virtual override onlyRole(KEEPER) {
        if (!isVaultSet) {
            revert VAULT_NOT_ATTACHED();
        }
        if (_amount == 0) {
            revert BORROW_AMOUNT_ZERO();
        }
        IVault(_vault).pull(_amount);
        totalDeposited += _amount;
        emit Borrow(_msgSender(), _amount, _vault, asset);
    }

    /**
     * @inheritdoc IStrategy
     */
    function repay() public virtual override onlyRole(KEEPER) {
        _repay();
    }

    function _repay() internal {
        if (!isVaultSet) {
            revert VAULT_NOT_ATTACHED();
        }
        uint256 unused = getUnused();
        if (unused > 0) {
            IVault(_vault).depositStrategyFunds(unused);
        }
        if (totalDeposited >= unused) {
            totalDeposited -= unused;
        } else {
            totalDeposited = 0;
        }
        emit Repay(_msgSender(), unused, _vault, asset);
    }
}

File 15 of 23 : IStrategy.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface IStrategy {
    // ============================= View functions ================================

    /**
     * @return strategy name
     */
    function name() external view returns (bytes32);

    /**
     * Returns the base erc20 asset for the strategy.
     * Assumption: For now, strategies only accept one base asset at the time (i.e the same strat cannot invest ETH and DPX ony one or the other).
     * @return the address for the asset
     */
    function asset() external view returns (address);

    /**
     * Returns the total deposited assets in the strategy.
     * @return the total amount of deposited assets.
     */
    function totalDeposited() external view returns (uint256);

    /**
     * Returns the current unused assets in the strategy.
     * @return unused amount of assets
     */
    function getUnused() external view returns (uint256);

    /**
     * Returns the vault attached to strategy.
     * Should revert with error if vault is not attached.
     */
    function getVault() external view returns (address);

    // ============================= Mutative functions ================================

    /**
     * Borrow base assets from the vault.
     * This will borrow the required `_amount` in base assets from the vault.
     * @dev SHOULD only be called by the strategists
     * @dev SHOULD call a very specific method on the vault and not do "transferTo"
     * @dev SHOULD emit event Borrow(vault, asset, amount)
     * @param _amount the amount of assets to borrow
     */
    function borrow(uint256 _amount) external;

    /**
     * Returns the funds back to the vault.
     * @dev SHOULD only be called by the strategists
     * @dev SHOULD call a very specific method on the vault "depositProfits"
     * @dev SHOULD emit event Repay(vault, asset, amount)
     */
    function repay() external;

    /**
     * Migrates funds to specified address `_to`.
     * @dev SHOULD only be called by the GOVERNOR.
     *
     * Emits {FundsMigrated}
     */
    function migrateFunds(
        address _to,
        address[] memory _tokens,
        bool _shouldTransferEth,
        bool _shouldTransferERC721
    ) external;

    /**
     * Detaches the strategy.
     * For some reason we might want to detach the strat from the vault,
     * this function should close all open positions, repay the vault and remove itself from the vault whitelist.
     *
     * Reverts if pending settlements or unable to withdraw every deposit after calling `repay`.
     * This is to ensure that the Strategy only detaches if everything is settled and
     * deposited assets are repaid to vault.
     *
     * Make sure to invoke `removeStrategyFromWhitelist` on previously detached vault after detaching.
     *
     * @dev SHOULD only be called by the `GOVERNOR`. Governor should also have `KEEPER` role in order to detach successfully.
     * @dev This function should raise an error in the case it can't withdrawal all the funds invested from the used contracts
     */
    function detach() external;

    /**
     * @dev Attaches `_vault` to this strategy.
     *
     * Only a strategist can attach vault and can only happen once.
     * This method is used over the constructor to prevent circular dependency.
     * Should revert with error if vault is already attached.
     *
     * Invoke `whitelistStrategy` on vault after calling this to whitelist this
     * strategy for the vault to be able to pull assets and perform other restricted actions.
     *
     * Emits {VaultSet}.
     */
    function setVault(address _vault) external;

    // ============================= Events ================================
    /**
     * Emitted when borrowing assets from the underlying vault.
     */
    event Borrow(
        address indexed strategist,
        uint256 amount,
        address indexed vault,
        address indexed asset
    );

    /**
     * Emitted when closing the strategy.
     */
    event Repay(
        address indexed strategist,
        uint256 amount,
        address indexed vault,
        address indexed asset
    );

    /**
     * Emitted when attaching the vault.
     */
    event VaultSet(address indexed governor, address indexed vault);

    /**
     * Emitted when migrating funds (ex in case of an emergency).
     */
    event FundsMigrated(address indexed governor);

    /**
     * Emitted when detaching the vault.
     */
    event VaultDetached(address indexed governor, address indexed vault);

    // ============================= Errors ================================
    error ADDRESS_CANNOT_BE_ZERO_ADDRESS();
    error VAULT_NOT_ATTACHED();
    error VAULT_ALREADY_ATTACHED();
    error MANAGEMENT_WINDOW_NOT_OPEN();
    error NOT_ENOUGH_AVAILABLE_ASSETS();
    error STRATEGY_STILL_HAS_ASSET_BALANCE();
    error BORROW_AMOUNT_ZERO();
    error MSG_SENDER_DOES_NOT_HAVE_PERMISSION_TO_EMERGENCY_WITHDRAW();
}

File 16 of 23 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 17 of 23 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 18 of 23 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 19 of 23 : IVault.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

interface IVault {
    // ============================= View functions ================================

    /**
     * The amount of `shares` that the Vault would exchange for the amount of `assets` provided, in an ideal scenario where all the conditions are met.
     *
     * Does not show any variations depending on the caller.
     * Does not reflect slippage or other on-chain conditions, when performing the actual exchange.
     * Does not revert unless due to integer overflow caused by an unreasonably large input.
     * This calculation does not reflect the “per-user” price-per-share, and instead reflects the “average-user’s” price-per-share, meaning what the average user can expect to see when exchanging to and from.
     *
     * @param assets Amount of assets to convert.
     * @return shares Amount of shares calculated for the amount of given assets, rounded down towards 0. Does not include any fees that are charged against assets in the Vault.
     */
    function convertToShares(uint256 assets)
        external
        view
        returns (uint256 shares);

    /**
     * The amount of `assets` that the Vault would exchange for the amount of `shares` provided, in an ideal scenario where all the conditions are met.
     *
     * Does not show any variations depending on the caller.
     * Does not reflect slippage or other on-chain conditions, when performing the actual exchange.
     * Does not revert unless due to integer overflow caused by an unreasonably large input.
     * This calculation does not reflect the “per-user” price-per-share, and instead reflects the “average-user’s” price-per-share, meaning what the average user can expect to see when exchanging to and from.
     *
     * @return assets Amount of assets calculated for the given amount of shares, rounded down towards 0. Does not include fees that are charged against assets in the Vault.
     */
    function convertToAssets(uint256 shares)
        external
        view
        returns (uint256 assets);

    /**
     * Maximum amount of the underlying asset that can be deposited into the Vault for the receiver, through a deposit call.
     * Returns the maximum amount of assets deposit would allow to be deposited for receiver and not cause a revert, which should be higher than the actual maximum that would be accepted (it should underestimate if necessary). This assumes that the user has infinite assets, i.e. does not rely on balanceOf of asset.
     *
     * Does not revert.
     * This is akin to `vaultCap` in legacy vaults.
     *
     * The `receiver` parameter is added for ERC-4626 parity and is not relevant to our use case
     * since we are not going to have user specific limits for deposits. Either deposits are limited
     * to everyone or no one.
     *
     * @return maxAssets Max assets that can be deposited for receiver. Returns 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. Returns 0 if deposits are entirely disabled (even temporarily).
     */
    function maxDeposit(address receiver)
        external
        view
        returns (uint256 maxAssets);

    /**
     * Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
     *
     * Returns as close to and no more than the exact amount of Vault shares that would be minted in a deposit call in the same transaction. I.e. deposit will return the same or more shares as previewDeposit if called in the same transaction.
     * Does not account for deposit limits like those returned from maxDeposit and always acts as though the deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * Does not revert due to vault specific user/global limits. May revert due to other conditions that would also cause deposit to revert.
     *
     * Any unfavorable discrepancy between convertToShares and previewDeposit will be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing.
     *
     * @return shares exact amount of shares that would be minted in a deposit call. That includes deposit fees. Integrators should be aware of the existence of deposit fees.
     */
    function previewDeposit(uint256 assets)
        external
        view
        returns (uint256 shares);

    /**
     * @return The current vault State
     */
    function state() external view returns (State);

    /**
     * The address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     */
    function asset() external view returns (address);

    /**
     * The address of the underlying shares token used used to represent tokenized vault.
     */
    function share() external view returns (address);

    /**
     * Total amount of the underlying asset that is managed by this vault.
     *
     * This includes any compounding that occurs from yield.
     * It must be inclusive of any fees that are charged against assets in the Vault.
     * Must not revert.
     *
     * @return totalManagedAssets amount of underlying asset managed by vault.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * Maximum amount of shares that can be minted from the Vault for the `receiver`, through a `mint` call.
     *
     * Returns `2 ** 256 - 1` if there is no limit on the maximum amount of shares that may be minted.
     */
    function maxMint(address receiver)
        external
        view
        returns (uint256 maxShares);

    /**
     * Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
     * MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause mint to revert.
     * note: Any unfavorable discrepancy between `convertToAssets` and `previewMint` should be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by minting.
     *
     * Does not account for mint limits like those returned from maxMint and always acts as though the mint would be accepted, regardless if the user has enough tokens approved, etc.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * Maximum amount of the underlying asset that can be withdrawn from the `owner` balance in the Vault, through a `withdraw` call.
     *
     * Factors in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it must return 0.
     * Does not revert.
     *
     * @return maxAssets The maximum amount of assets that could be transferred from `owner` through `withdraw` and not cause a revert, which must not be higher than the actual maximum that would be accepted (it should underestimate if necessary).
     */
    function maxWithdraw(address owner)
        external
        view
        returns (uint256 maxAssets);

    /**
     * Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
     *
     * Does not revert due to vault specific user/global limits. May revert due to other conditions that would also cause withdraw to revert.
     * Any unfavorable discrepancy between convertToShares and previewWithdraw should be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing.
     *
     * @return shares Shares available to withdraw for specified assets. This includes of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     */
    function previewWithdraw(uint256 assets)
        external
        view
        returns (uint256 shares);

    /**
     * Maximum amount of Vault shares that can be redeemed from the `owner` balance in the Vault, through a `redeem` call.
     *
     * @return maxShares Max shares that can be redeemed. Factors in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it will return 0.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
     * Does not account for redemption limits like those returned from maxRedeem and should always act as though the redemption would be accepted, regardless if the user has enough shares, etc.
     *
     * Does not revert due to vault specific user/global limits. May revert due to other conditions that would also cause redeem to revert.
     *
     * @return assets Amount of assets redeemable for given shares. Includes of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     */
    function previewRedeem(uint256 shares)
        external
        view
        returns (uint256 assets);

    // ============================= User functions ================================

    /**
     * @dev Mints `shares` Vault shares to `receiver` by depositing `amount` of underlying tokens. This should only be called outside the management window.
     *
     * Reverts if all of assets cannot be deposited (ex due to deposit limit, slippage, approvals, etc).
     *
     * Emits a {Deposit} event
     */
    function deposit(uint256 assets, address receiver)
        external
        returns (uint256 shares);

    /**
     * Mints exactly `shares` Vault shares to `receiver` by depositing `amount` of underlying tokens.
     *
     * Reverts if all of shares cannot be minted (ex. due to deposit limit being reached, slippage, etc).
     *
     * Emits a {Deposit} event
     */
    function mint(uint256 shares, address receiver)
        external
        returns (uint256 assets);

    /**
     * Burns `shares` from `owner` and sends exactly `assets` of underlying tokens to `receiver`. Only available outside of management window.
     *
     * Reverts if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc).
     * Any pre-requesting methods before withdrawal should be performed separately.
     *
     * Emits a {Withdraw} event
     */
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    /**
     * Burns exactly `shares` from `owner` and sends `assets` of underlying tokens to `receiver`. Only available outside of management window.
     *
     * Reverts if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc).
     * Any pre-requesting methods before withdrawal should be performed separately.
     *
     * Emits a {Withdraw} event
     */
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    // ============================= Strategy functions ================================

    /**
     * Sends the required amount of Asset from this vault to the calling strategy.
     * @dev can only be called by whitelisted strategies (KEEPER role)
     * @dev reverts if management window is closed.
     * @param assets the amount of tokens to pull
     */
    function pull(uint256 assets) external;

    /**
     * Deposits funds from Strategy (both profits and principal amounts).
     * @dev can only be called by whitelisted strategies (KEEPER role)
     * @dev reverts if management window is closed.
     * @param assets the amount of Assets being deposited from the strategy.
     */
    function depositStrategyFunds(uint256 assets) external;

    // ============================= Admin functions ================================

    /**
     * Sets the max deposit `amount` for vault. Akin to setting vault cap in v2 vaults.
     * Since we will not be limiting deposits per user there is no need to add `receiver` input
     * in the argument.
     */
    function setVaultCap(uint256 amount) external;

    /**
     * Adds a strategy to the whitelist.
     * @dev can only be called by governor (GOVERNOR role)
     * @param _address of the strategy to whitelist
     */
    function whitelistStrategy(address _address) external;

    /**
     * Removes a strategy from the whitelist.
     * @dev can only be called by governor (GOVERNOR role)
     * @param _address of the strategy to remove from whitelist
     */
    function removeStrategyFromWhitelist(address _address) external;

    /**
     * @notice Adds a contract to the whitelist.
     * @dev By default only EOA cann interact with the vault.
     * @dev Whitelisted contracts will be able to interact with the vault too.
     * @param contractAddress The address of the contract to whitelist.
     */
    function addContractAddressToWhitelist(address contractAddress) external;

    /**
     * @notice Used to check wheter a contract address is whitelisted to use the vault
     * @param _contractAddress The address of the contract to check
     * @return `true` if the contract is whitelisted, `false` otherwise
     */
    function whitelistedContract(address _contractAddress)
        external
        view
        returns (bool);

    /**
     * @notice Removes a contract from the whitelist.
     * @dev Removed contracts wont be able to interact with the vault.
     * @param contractAddress The address of the contract to whitelist.
     */
    function removeContractAddressFromWhitelist(address contractAddress)
        external;

    /**
     * Migrate vault to new vault contract.
     * @dev acts as emergency withdrawal if needed.
     * @dev can only be called by governor (GOVERNOR role)
     * @param _to New vault contract address.
     * @param _tokens Addresses of tokens to be migrated.
     *
     */
    function migrate(address _to, address[] memory _tokens) external;

    /**
     * Deposits and withdrawals close, assets are under vault control.
     * @dev can only be called by governor (GOVERNOR role)
     */
    function openManagementWindow() external;

    /**
     * Open vault for deposits and claims.
     * @dev can only be called by governor (GOVERNOR role)
     */
    function closeManagementWindow() external;

    /**
     * Open vault for deposits and claims, sets the snapshot of assets balance manually
     * @dev can only be called by governor (GOVERNOR role)
     * @dev can only be called on `State.INITIAL`
     * @param _snapshotAssetBalance Overrides the value of the snapshotted asset balance
     * @param _snapshotShareSupply Overrides the value of the snapshotted share supply
     */
    function initialRun(
        uint256 _snapshotAssetBalance,
        uint256 _snapshotShareSupply
    ) external;

    /**
     * Enable/diable charging performance & management fees
     * @dev can only be called by GOVERNOR role
     * @param _status `true` if the vault should charge fees, `false` otherwise
     */
    function setChargeFees(bool _status) external;

    /**
     * Updated the fee distributor address
     * @dev can only be called by GOVERNOR role
     * @param _feeDistributor The address of the new fee distributor
     */
    function setFeeDistributor(address _feeDistributor) external;

    // ============================= Enums =================================

    /**
     * Enum to represent the current state of the vault
     * INITIAL = Right after deployment, can move to `UNMANAGED` by calling `initialRun`
     * UNMANAGED = Users are able to interact with the vault, can move to `MANAGED` by calling `openManagementWindow`
     * MANAGED = Strategies will be able to borrow & repay, can move to `UNMANAGED` by calling `closeManagementWindow`
     */
    enum State {
        INITIAL,
        UNMANAGED,
        MANAGED
    }

    // ============================= Events ================================

    /**
     * `caller` has exchanged `assets` for `shares`, and transferred those `shares` to `owner`.
     * Emitted when tokens are deposited into the Vault via the `mint` and `deposit` methods.
     */
    event Deposit(
        address indexed caller,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * `caller` has exchanged `shares`, owned by `owner`, for `assets`, and transferred those `assets` to `receiver`.
     * Will be emitted when shares are withdrawn from the Vault in `ERC4626.redeem` or `ERC4626.withdraw` methods.
     */
    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * emitted when vault balance snapshot is taken
     * @param _timestamp snapshot timestamp (indexed)
     * @param _vaultBalance vault balance value
     * @param _jonesAssetSupply jDPX total supply value
     */
    event Snapshot(
        uint256 indexed _timestamp,
        uint256 _vaultBalance,
        uint256 _jonesAssetSupply
    );

    /**
     * emitted when asset management window is opened
     * @param _timestamp snapshot timestamp (indexed)
     * @param _assetBalance new vault balance value
     * @param _shareSupply share token total supply at this time
     */
    event EpochStarted(
        uint256 indexed _timestamp,
        uint256 _assetBalance,
        uint256 _shareSupply
    );

    /** emitted when claim and deposit windows are open
     * @param _timestamp snapshot timestamp (indexed)
     * @param _assetBalance new vault balance value
     * @param _shareSupply share token total supply at this time
     */
    event EpochEnded(
        uint256 indexed _timestamp,
        uint256 _assetBalance,
        uint256 _shareSupply
    );

    // ============================= Errors ================================
    error MSG_SENDER_NOT_WHITELISTED_USER();
    error DEPOSIT_ASSET_AMOUNT_EXCEEDS_MAX_DEPOSIT();
    error MINT_SHARE_AMOUNT_EXCEEDS_MAX_MINT();
    error ZERO_SHARES_AVAILABLE_WHEN_DEPOSITING();
    error INVALID_STATE(State _expected, State _actual);
    error INVALID_ASSETS_AMOUNT();
    error INVALID_SHARES_AMOUNT();
    error CONTRACT_ADDRESS_MAKING_PROHIBITED_FUNCTION_CALL();
    error INVALID_ADDRESS();
    error INVALID_SNAPSHOT_VALUE();
}

File 20 of 23 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 21 of 23 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 22 of 23 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

File 23 of 23 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

Settings
{
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/VaultsV2/library/SushiRouterWrapper.sol": {
      "SushiRouterWrapper": "0xbcc4ae91d75fbe79606555f3f402ce753d2a95ba"
    },
    "contracts/VaultsV3/library/SsovV3Wrapper.sol": {
      "SsovV3Wrapper": "0x7487c19df56b7ea764fc269468b5d3014565052f"
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"bytes32","name":"_name","type":"bytes32"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_SSOV","type":"address"},{"internalType":"address","name":"_viewer","type":"address"},{"internalType":"address","name":"_router","type":"address"},{"internalType":"address","name":"_governor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ADDRESS_CANNOT_BE_ZERO_ADDRESS","type":"error"},{"inputs":[],"name":"BORROW_AMOUNT_ZERO","type":"error"},{"inputs":[],"name":"MANAGEMENT_WINDOW_NOT_OPEN","type":"error"},{"inputs":[],"name":"MSG_SENDER_DOES_NOT_HAVE_PERMISSION_TO_EMERGENCY_WITHDRAW","type":"error"},{"inputs":[],"name":"NOT_ENOUGH_AVAILABLE_ASSETS","type":"error"},{"inputs":[],"name":"STRATEGY_STILL_HAS_ASSET_BALANCE","type":"error"},{"inputs":[],"name":"VAULT_ALREADY_ATTACHED","type":"error"},{"inputs":[],"name":"VAULT_NOT_ATTACHED","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategist","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"governor","type":"address"}],"name":"FundsMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategist","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"governor","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"}],"name":"VaultDetached","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"governor","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"}],"name":"VaultSet","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOVERNOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SSOV","outputs":[{"internalType":"contract ISsovV3","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SSOVViewer","outputs":[{"internalType":"contract ISsovV3Viewer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strikeIndex","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositSSOV","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_strikeIndices","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"depositSSOVMultiple","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"detach","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnused","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"grantKeeperRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isVaultSet","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"bool","name":"_shouldTransferEth","type":"bool"},{"internalType":"bool","name":"_shouldTransferERC721","type":"bool"}],"name":"migrateFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_strikeIndex","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"purchaseOption","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"}],"name":"revokeKeeperRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"contract ISsovV3Router","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewardTokens","type":"address[]"},{"internalType":"uint256[]","name":"_minBaseOutputAmounts","type":"uint256[]"}],"name":"sellRewardTokensForBaseToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newVault","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ssovEpoch","type":"uint256"},{"internalType":"uint256[]","name":"_ssovStrikes","type":"uint256[]"}],"name":"settleEpoch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDeposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISsovV3","name":"_newSSOV","type":"address"}],"name":"updateSSOVAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"viewer","outputs":[{"internalType":"contract ISsovV3Viewer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"uint256[]","name":"_strikes","type":"uint256[]"}],"name":"withdrawEpoch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdrawTokenId","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

60c06040523480156200001157600080fd5b506040516200581338038062005813833981810160405281019062000037919062000ac7565b858585858585858582600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620000a8576040517f9cfbf00600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000110576040517f9cfbf00600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8260a081815250508173ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250506200017e7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a82620004ac60201b60201c565b620001b07f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad183682620004ac60201b60201c565b505050600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614156200021b576040517f9cfbf00600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141562000283576040517f9cfbf00600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620002eb576040517f9cfbf00600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83600360016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050505050506200042a600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60805173ffffffffffffffffffffffffffffffffffffffff166200059d60201b62002382179092919060201c565b620004a0600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60805173ffffffffffffffffffffffffffffffffffffffff166200059d60201b62002382179092919060201c565b50505050505062001023565b620004be8282620006ff60201b60201c565b6200059957600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506200053e6200076960201b60201c565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b60008114806200062d575060008373ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e30856040518363ffffffff1660e01b8152600401620005e792919062000b74565b602060405180830381865afa15801562000605573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200062b919062000bdc565b145b6200066f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620006669062000c95565b60405180910390fd5b620006fa8363095ea7b360e01b84846040516024016200069192919062000cc8565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506200077160201b60201c565b505050565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b600033905090565b6000620007da826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166200084560201b620024d1179092919060201c565b9050600081511115620008405780806020019051810190620007fd919062000d32565b6200083f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620008369062000dda565b60405180910390fd5b5b505050565b60606200085c84846000856200086560201b60201c565b90509392505050565b606082471015620008ad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620008a49062000e72565b60405180910390fd5b620008be856200099360201b60201c565b62000900576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620008f79062000ee4565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516200092b919062000f89565b60006040518083038185875af1925050503d80600081146200096a576040519150601f19603f3d011682016040523d82523d6000602084013e6200096f565b606091505b509150915062000987828286620009b660201b60201c565b92505050949350505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b60608315620009c85782905062000a1b565b600083511115620009dc5782518084602001fd5b816040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000a12919062000fff565b60405180910390fd5b9392505050565b600080fd5b6000819050919050565b62000a3c8162000a27565b811462000a4857600080fd5b50565b60008151905062000a5c8162000a31565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000a8f8262000a62565b9050919050565b62000aa18162000a82565b811462000aad57600080fd5b50565b60008151905062000ac18162000a96565b92915050565b60008060008060008060c0878903121562000ae75762000ae662000a22565b5b600062000af789828a0162000a4b565b965050602062000b0a89828a0162000ab0565b955050604062000b1d89828a0162000ab0565b945050606062000b3089828a0162000ab0565b935050608062000b4389828a0162000ab0565b92505060a062000b5689828a0162000ab0565b9150509295509295509295565b62000b6e8162000a82565b82525050565b600060408201905062000b8b600083018562000b63565b62000b9a602083018462000b63565b9392505050565b6000819050919050565b62000bb68162000ba1565b811462000bc257600080fd5b50565b60008151905062000bd68162000bab565b92915050565b60006020828403121562000bf55762000bf462000a22565b5b600062000c058482850162000bc5565b91505092915050565b600082825260208201905092915050565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60008201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000602082015250565b600062000c7d60368362000c0e565b915062000c8a8262000c1f565b604082019050919050565b6000602082019050818103600083015262000cb08162000c6e565b9050919050565b62000cc28162000ba1565b82525050565b600060408201905062000cdf600083018562000b63565b62000cee602083018462000cb7565b9392505050565b60008115159050919050565b62000d0c8162000cf5565b811462000d1857600080fd5b50565b60008151905062000d2c8162000d01565b92915050565b60006020828403121562000d4b5762000d4a62000a22565b5b600062000d5b8482850162000d1b565b91505092915050565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b600062000dc2602a8362000c0e565b915062000dcf8262000d64565b604082019050919050565b6000602082019050818103600083015262000df58162000db3565b9050919050565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f60008201527f722063616c6c0000000000000000000000000000000000000000000000000000602082015250565b600062000e5a60268362000c0e565b915062000e678262000dfc565b604082019050919050565b6000602082019050818103600083015262000e8d8162000e4b565b9050919050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b600062000ecc601d8362000c0e565b915062000ed98262000e94565b602082019050919050565b6000602082019050818103600083015262000eff8162000ebd565b9050919050565b600081519050919050565b600081905092915050565b60005b8381101562000f3c57808201518184015260208101905062000f1f565b8381111562000f4c576000848401525b50505050565b600062000f5f8262000f06565b62000f6b818562000f11565b935062000f7d81856020860162000f1c565b80840191505092915050565b600062000f97828462000f52565b915081905092915050565b600081519050919050565b6000601f19601f8301169050919050565b600062000fcb8262000fa2565b62000fd7818562000c0e565b935062000fe981856020860162000f1c565b62000ff48162000fad565b840191505092915050565b600060208201905081810360008301526200101b818462000fbe565b905092915050565b60805160a0516147846200108f600039600061074d01526000818161099501528181610d59015281816110d30152818161145c0152818161152a0152818161167c01528181611aea01528181611dc8015281816121110152818161227b015261268201526147846000f3fe608060405234801561001057600080fd5b506004361061021c5760003560e01c80638d928af811610125578063c5ebeaec116100ad578063ea0ab0561161007c578063ea0ab0561461063b578063f242862114610659578063f30878c114610677578063f887ea4014610695578063ff50abdc146106b35761021c565b8063c5ebeaec146105c9578063c7fbe3a8146105e5578063cc1f071a14610603578063d547741f1461061f5761021c565b8063a217fddf116100f4578063a217fddf14610527578063b34e837814610545578063b6e2dc3514610561578063bb6ad6ef1461057d578063bdaa4aa4146105ad5761021c565b80638d928af81461049d57806391d14854146104bb57806399c64859146104eb5780639abbde03146105095761021c565b80633824c59c116101a857806360e9791a1161017757806360e9791a146103e55780636817031b146104155780636dc0ae22146104315780637b1ae4361461044f578063862a179e1461047f5761021c565b80633824c59c1461037157806338d52e0f146103a1578063402d8883146103bf5780634106d274146103c95761021c565b8063225435c0116101ef578063225435c0146102cf578063248a9ca3146102d957806326882c57146103095780632f2ff15d1461033957806336568abe146103555761021c565b806301ffc9a71461022157806306fdde0314610251578063150b7a021461026f57806315fa7e721461029f575b600080fd5b61023b60048036038101906102369190612f41565b6106d1565b6040516102489190612f89565b60405180910390f35b61025961074b565b6040516102669190612fbd565b60405180910390f35b610289600480360381019061028491906130d1565b61076f565b6040516102969190613168565b60405180910390f35b6102b960048036038101906102b49190613183565b61079d565b6040516102c69190612f89565b60405180910390f35b6102d761088a565b005b6102f360048036038101906102ee91906131ef565b610a94565b6040516103009190612fbd565b60405180910390f35b610323600480360381019061031e919061336b565b610ab3565b6040516103309190612f89565b60405180910390f35b610353600480360381019061034e91906133c7565b610bc7565b005b61036f600480360381019061036a91906133c7565b610be8565b005b61038b60048036038101906103869190613407565b610c6b565b6040516103989190612f89565b60405180910390f35b6103a9610d57565b6040516103b6919061348e565b60405180910390f35b6103c7610d7b565b005b6103e360048036038101906103de91906134a9565b610db0565b005b6103ff60048036038101906103fa919061336b565b610e6f565b60405161040c9190612f89565b60405180910390f35b61042f600480360381019061042a91906134a9565b610f71565b005b6104396111b9565b6040516104469190612fbd565b60405180910390f35b61046960048036038101906104649190613183565b6111dd565b60405161047691906134e5565b60405180910390f35b6104876112ca565b6040516104949190612fbd565b60405180910390f35b6104a56112ee565b6040516104b2919061348e565b60405180910390f35b6104d560048036038101906104d091906133c7565b61135e565b6040516104e29190612f89565b60405180910390f35b6104f36113c8565b604051610500919061355f565b60405180910390f35b6105116113ee565b60405161051e9190612f89565b60405180910390f35b61052f611401565b60405161053c9190612fbd565b60405180910390f35b61055f600480360381019061055a91906135b8565b611408565b005b61057b600480360381019061057691906136d4565b611572565b005b61059760048036038101906105929190613757565b611939565b6040516105a49190612f89565b60405180910390f35b6105c760048036038101906105c29190613784565b611a15565b005b6105e360048036038101906105de9190613757565b611fbe565b005b6105ed6121d8565b6040516105fa919061381d565b60405180910390f35b61061d600480360381019061061891906134a9565b6121fe565b005b610639600480360381019061063491906133c7565b612256565b005b610643612277565b60405161065091906134e5565b60405180910390f35b610661612318565b60405161066e919061348e565b60405180910390f35b61067f612330565b60405161068c919061355f565b60405180910390f35b61069d612356565b6040516106aa9190613859565b60405180910390f35b6106bb61237c565b6040516106c891906134e5565b60405180910390f35b60007f7965db0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806107445750610743826124e9565b5b9050919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60007f150b7a023d4804d13e8c85fb27262cb750cf6ba9f9dd3bb30d90f482ceeb4b1f905095945050505050565b60007f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad18366107c981612553565b600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16737487c19df56b7ea764fc269468b5d3014565052f6345e5edf990918686306040518563ffffffff1660e01b815260040161084094939291906138a1565b602060405180830381865af415801561085d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088191906138fb565b91505092915050565b7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a6108b481612553565b600360009054906101000a900460ff166108fa576040517f23eecdf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610902612567565b600061090c612277565b1115610944576040517f93224eac00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506109d9600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166123829092919063ffffffff16565b6000600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600360006101000a81548160ff0219169083151502179055508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f99f736dac5b359a472c9b2edd93e35b98547bf61d1ee8395824e748c7731cc7860405160405180910390a35050565b6000806000838152602001908152602001600020600101549050919050565b60007f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836610adf81612553565b600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16737487c19df56b7ea764fc269468b5d3014565052f636f5bf2809091600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff163088886040518663ffffffff1660e01b8152600401610b7a9594939291906139f5565b602060405180830381865af4158015610b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbb91906138fb565b50600191505092915050565b610bd082610a94565b610bd981612553565b610be38383612748565b505050565b610bf0612828565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610c5d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c5490613ad2565b60405180910390fd5b610c678282612830565b5050565b60007f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836610c9781612553565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b942f9a1858530600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518563ffffffff1660e01b8152600401610d1a9493929190613b88565b600060405180830381600087803b158015610d3457600080fd5b505af1158015610d48573d6000803e3d6000fd5b50505050600191505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836610da581612553565b610dad612567565b50565b7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a610dda81612553565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610e41576040517f9cfbf00600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e6b7f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad183683612748565b5050565b60007f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836610e9b81612553565b600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16737487c19df56b7ea764fc269468b5d3014565052f6358bd17679091600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168787306040518663ffffffff1660e01b8152600401610f36959493929190613bdb565b60006040518083038186803b158015610f4e57600080fd5b505af4158015610f62573d6000803e3d6000fd5b50505050600191505092915050565b7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a610f9b81612553565b600360009054906101000a900460ff1615610fe2576040517fe1b7a65400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611049576040517f9cfbf00600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611117600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166123829092919063ffffffff16565b6001600360006101000a81548160ff021916908315150217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16611173612828565b73ffffffffffffffffffffffffffffffffffffffff167f8800deb8c31293b539eaf5391fcc88280dc58f015c043d65dd5b72a0979a1dd160405160405180910390a35050565b7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a81565b60007f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad183661120981612553565b600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16737487c19df56b7ea764fc269468b5d3014565052f6384d53f0590918686306040518563ffffffff1660e01b815260040161128094939291906138a1565b602060405180830381865af415801561129d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112c19190613c4a565b91505092915050565b7f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad183681565b6000600360009054906101000a900460ff16611336576040517f23eecdf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360009054906101000a900460ff1681565b6000801b81565b7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a61143281612553565b6114a0600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166123829092919063ffffffff16565b81600360016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061156e600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166123829092919063ffffffff16565b5050565b7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a61159c81612553565b60005b84518110156116ec5760008582815181106115bd576115bc613c77565b5b6020026020010151905060008173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611602919061348e565b602060405180830381865afa15801561161f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116439190613c4a565b9050600081111561167a5761167988828473ffffffffffffffffffffffffffffffffffffffff166129119092919063ffffffff16565b5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156116d75760006002819055505b505080806116e490613cd5565b91505061159f565b5060004790506000811180156116ff5750835b1561174c578573ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561174a573d6000803e3d6000fd5b505b82156118ee576000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663770b478a30600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518363ffffffff1660e01b81526004016117d3929190613d1e565b600060405180830381865afa1580156117f0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906118199190613dde565b905060005b81518110156118eb57600082828151811061183c5761183b613c77565b5b60200260200101519050600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166342842e0e308b846040518463ffffffff1660e01b81526004016118a593929190613e27565b600060405180830381600087803b1580156118bf57600080fd5b505af11580156118d3573d6000803e3d6000fd5b505050505080806118e390613cd5565b91505061181e565b50505b8573ffffffffffffffffffffffffffffffffffffffff167f0e3e9a671666295c299b941a07625839915442794bf73a484b24bb3e221270c360405160405180910390a2505050505050565b60007f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad183661196581612553565b600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1662f714ce84306040518363ffffffff1660e01b81526004016119c1929190613e5e565b6000604051808303816000875af11580156119e0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190611a099190613e87565b50506001915050919050565b7f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836611a3f81612553565b6000835167ffffffffffffffff811115611a5c57611a5b61322d565b5b604051908082528060200260200182016040528015611a8f57816020015b6060815260200190600190039081611a7a5790505b50905060005b8451811015611f1c576000858281518110611ab357611ab2613c77565b5b602002602001015190507382af49447d8a07e3bd95bd0d56f35241523fbab173ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff161415611c7857600267ffffffffffffffff811115611b4057611b3f61322d565b5b604051908082528060200260200182016040528015611b6e5781602001602082028036833780820191505090505b50838381518110611b8257611b81613c77565b5b602002602001018190525080838381518110611ba157611ba0613c77565b5b6020026020010151600081518110611bbc57611bbb613c77565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250507382af49447d8a07e3bd95bd0d56f35241523fbab1838381518110611c1e57611c1d613c77565b5b6020026020010151600181518110611c3957611c38613c77565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050611e50565b600367ffffffffffffffff811115611c9357611c9261322d565b5b604051908082528060200260200182016040528015611cc15781602001602082028036833780820191505090505b50838381518110611cd557611cd4613c77565b5b602002602001018190525080838381518110611cf457611cf3613c77565b5b6020026020010151600081518110611d0f57611d0e613c77565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250507382af49447d8a07e3bd95bd0d56f35241523fbab1838381518110611d7157611d70613c77565b5b6020026020010151600181518110611d8c57611d8b613c77565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250507f0000000000000000000000000000000000000000000000000000000000000000838381518110611dfa57611df9613c77565b5b6020026020010151600281518110611e1557611e14613c77565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b611f08731b02da8cb0d097eb8d57a175b88c7d8b479975068273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611ea1919061348e565b602060405180830381865afa158015611ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee29190613c4a565b8373ffffffffffffffffffffffffffffffffffffffff166123829092919063ffffffff16565b508080611f1490613cd5565b915050611a95565b50731b02da8cb0d097eb8d57a175b88c7d8b4799750673ffffffffffffffffffffffffffffffffffffffff1673bcc4ae91d75fbe79606555f3f402ce753d2a95ba63fc334d009091858730866040518663ffffffff1660e01b8152600401611f889594939291906140f3565b60006040518083038186803b158015611fa057600080fd5b505af4158015611fb4573d6000803e3d6000fd5b5050505050505050565b7f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad1836611fe881612553565b600360009054906101000a900460ff1661202e576040517f23eecdf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000821415612069576040517fcd6bbd7100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16634d0392a8836040518263ffffffff1660e01b81526004016120c491906134e5565b600060405180830381600087803b1580156120de57600080fd5b505af11580156120f2573d6000803e3d6000fd5b505050508160026000828254612108919061415b565b925050819055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16612187612828565b73ffffffffffffffffffffffffffffffffffffffff167face89dcec09c69462cb37ba9dbe6990b9cb4cc4b7474f1cc0574f0ac86497086856040516121cc91906134e5565b60405180910390a45050565b600360019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b7f1a6838efa4183e08fe3607359d1259272af9d4716f65e1a7b5921f78fd5a3c6a61222881612553565b6122527f71a9859d7dd21b24504a6f306077ffc2d510b4d4b61128e931fe937441ad183683612830565b5050565b61225f82610a94565b61226881612553565b6122728383612830565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016122d2919061348e565b602060405180830381865afa1580156122ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123139190613c4a565b905090565b7382af49447d8a07e3bd95bd0d56f35241523fbab181565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60025481565b600081148061240c575060008373ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e30856040518363ffffffff1660e01b81526004016123c99291906141b1565b602060405180830381865afa1580156123e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061240a9190613c4a565b145b61244b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124429061424c565b60405180910390fd5b6124cc8363095ea7b360e01b848460405160240161246a92919061426c565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612997565b505050565b60606124e08484600085612a5e565b90509392505050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6125648161255f612828565b612b72565b50565b600360009054906101000a900460ff166125ad576040517f23eecdf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006125b7612277565b9050600081111561265057600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e8ddb9a6826040518263ffffffff1660e01b815260040161261d91906134e5565b600060405180830381600087803b15801561263757600080fd5b505af115801561264b573d6000803e3d6000fd5b505050505b806002541061267757806002600082825461266b9190614295565b92505081905550612680565b60006002819055505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166126f8612828565b73ffffffffffffffffffffffffffffffffffffffff167f7fbb649ddb1a6bb483960fc034d326a89c2c22de1d59cf00a85e70cbe98619c98460405161273d91906134e5565b60405180910390a450565b612752828261135e565b61282457600160008084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506127c9612828565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b600033905090565b61283a828261135e565b1561290d57600080600084815260200190815260200160002060000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506128b2612828565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45b5050565b6129928363a9059cbb60e01b848460405160240161293092919061426c565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612997565b505050565b60006129f9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166124d19092919063ffffffff16565b9050600081511115612a595780806020019051810190612a1991906138fb565b612a58576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a4f9061433b565b60405180910390fd5b5b505050565b606082471015612aa3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a9a906143cd565b60405180910390fd5b612aac85612c0f565b612aeb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ae290614439565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051612b1491906144d3565b60006040518083038185875af1925050503d8060008114612b51576040519150601f19603f3d011682016040523d82523d6000602084013e612b56565b606091505b5091509150612b66828286612c32565b92505050949350505050565b612b7c828261135e565b612c0b57612ba18173ffffffffffffffffffffffffffffffffffffffff166014612c99565b612baf8360001c6020612c99565b604051602001612bc09291906145c9565b6040516020818303038152906040526040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c02919061463c565b60405180910390fd5b5050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b60608315612c4257829050612c92565b600083511115612c555782518084602001fd5b816040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612c89919061463c565b60405180910390fd5b9392505050565b606060006002836002612cac919061465e565b612cb6919061415b565b67ffffffffffffffff811115612ccf57612cce61322d565b5b6040519080825280601f01601f191660200182016040528015612d015781602001600182028036833780820191505090505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110612d3957612d38613c77565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110612d9d57612d9c613c77565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060006001846002612ddd919061465e565b612de7919061415b565b90505b6001811115612e87577f3031323334353637383961626364656600000000000000000000000000000000600f861660108110612e2957612e28613c77565b5b1a60f81b828281518110612e4057612e3f613c77565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600485901c945080612e80906146b8565b9050612dea565b5060008414612ecb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ec29061472e565b60405180910390fd5b8091505092915050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b612f1e81612ee9565b8114612f2957600080fd5b50565b600081359050612f3b81612f15565b92915050565b600060208284031215612f5757612f56612edf565b5b6000612f6584828501612f2c565b91505092915050565b60008115159050919050565b612f8381612f6e565b82525050565b6000602082019050612f9e6000830184612f7a565b92915050565b6000819050919050565b612fb781612fa4565b82525050565b6000602082019050612fd26000830184612fae565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061300382612fd8565b9050919050565b61301381612ff8565b811461301e57600080fd5b50565b6000813590506130308161300a565b92915050565b6000819050919050565b61304981613036565b811461305457600080fd5b50565b60008135905061306681613040565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126130915761309061306c565b5b8235905067ffffffffffffffff8111156130ae576130ad613071565b5b6020830191508360018202830111156130ca576130c9613076565b5b9250929050565b6000806000806000608086880312156130ed576130ec612edf565b5b60006130fb88828901613021565b955050602061310c88828901613021565b945050604061311d88828901613057565b935050606086013567ffffffffffffffff81111561313e5761313d612ee4565b5b61314a8882890161307b565b92509250509295509295909350565b61316281612ee9565b82525050565b600060208201905061317d6000830184613159565b92915050565b6000806040838503121561319a57613199612edf565b5b60006131a885828601613057565b92505060206131b985828601613057565b9150509250929050565b6131cc81612fa4565b81146131d757600080fd5b50565b6000813590506131e9816131c3565b92915050565b60006020828403121561320557613204612edf565b5b6000613213848285016131da565b91505092915050565b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6132658261321c565b810181811067ffffffffffffffff821117156132845761328361322d565b5b80604052505050565b6000613297612ed5565b90506132a3828261325c565b919050565b600067ffffffffffffffff8211156132c3576132c261322d565b5b602082029050602081019050919050565b60006132e76132e2846132a8565b61328d565b9050808382526020820190506020840283018581111561330a57613309613076565b5b835b81811015613333578061331f8882613057565b84526020840193505060208101905061330c565b5050509392505050565b600082601f8301126133525761335161306c565b5b81356133628482602086016132d4565b91505092915050565b6000806040838503121561338257613381612edf565b5b600061339085828601613057565b925050602083013567ffffffffffffffff8111156133b1576133b0612ee4565b5b6133bd8582860161333d565b9150509250929050565b600080604083850312156133de576133dd612edf565b5b60006133ec858286016131da565b92505060206133fd85828601613021565b9150509250929050565b6000806040838503121561341e5761341d612edf565b5b600083013567ffffffffffffffff81111561343c5761343b612ee4565b5b6134488582860161333d565b925050602083013567ffffffffffffffff81111561346957613468612ee4565b5b6134758582860161333d565b9150509250929050565b61348881612ff8565b82525050565b60006020820190506134a3600083018461347f565b92915050565b6000602082840312156134bf576134be612edf565b5b60006134cd84828501613021565b91505092915050565b6134df81613036565b82525050565b60006020820190506134fa60008301846134d6565b92915050565b6000819050919050565b600061352561352061351b84612fd8565b613500565b612fd8565b9050919050565b60006135378261350a565b9050919050565b60006135498261352c565b9050919050565b6135598161353e565b82525050565b60006020820190506135746000830184613550565b92915050565b600061358582612ff8565b9050919050565b6135958161357a565b81146135a057600080fd5b50565b6000813590506135b28161358c565b92915050565b6000602082840312156135ce576135cd612edf565b5b60006135dc848285016135a3565b91505092915050565b600067ffffffffffffffff821115613600576135ff61322d565b5b602082029050602081019050919050565b600061362461361f846135e5565b61328d565b9050808382526020820190506020840283018581111561364757613646613076565b5b835b81811015613670578061365c8882613021565b845260208401935050602081019050613649565b5050509392505050565b600082601f83011261368f5761368e61306c565b5b813561369f848260208601613611565b91505092915050565b6136b181612f6e565b81146136bc57600080fd5b50565b6000813590506136ce816136a8565b92915050565b600080600080608085870312156136ee576136ed612edf565b5b60006136fc87828801613021565b945050602085013567ffffffffffffffff81111561371d5761371c612ee4565b5b6137298782880161367a565b935050604061373a878288016136bf565b925050606061374b878288016136bf565b91505092959194509250565b60006020828403121561376d5761376c612edf565b5b600061377b84828501613057565b91505092915050565b6000806040838503121561379b5761379a612edf565b5b600083013567ffffffffffffffff8111156137b9576137b8612ee4565b5b6137c58582860161367a565b925050602083013567ffffffffffffffff8111156137e6576137e5612ee4565b5b6137f28582860161333d565b9150509250929050565b60006138078261352c565b9050919050565b613817816137fc565b82525050565b6000602082019050613832600083018461380e565b92915050565b60006138438261352c565b9050919050565b61385381613838565b82525050565b600060208201905061386e600083018461384a565b92915050565b61387d816137fc565b82525050565b61388c81613036565b82525050565b61389b81612ff8565b82525050565b60006080820190506138b66000830187613874565b6138c36020830186613883565b6138d06040830185613883565b6138dd6060830184613892565b95945050505050565b6000815190506138f5816136a8565b92915050565b60006020828403121561391157613910612edf565b5b600061391f848285016138e6565b91505092915050565b6139318161353e565b82525050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61396c81613036565b82525050565b600061397e8383613963565b60208301905092915050565b6000602082019050919050565b60006139a282613937565b6139ac8185613942565b93506139b783613953565b8060005b838110156139e85781516139cf8882613972565b97506139da8361398a565b9250506001810190506139bb565b5085935050505092915050565b600060a082019050613a0a6000830188613874565b613a176020830187613928565b613a246040830186613892565b613a316060830185613883565b8181036080830152613a438184613997565b90509695505050505050565b600082825260208201905092915050565b7f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560008201527f20726f6c657320666f722073656c660000000000000000000000000000000000602082015250565b6000613abc602f83613a4f565b9150613ac782613a60565b604082019050919050565b60006020820190508181036000830152613aeb81613aaf565b9050919050565b600082825260208201905092915050565b613b0c81613036565b82525050565b6000613b1e8383613b03565b60208301905092915050565b6000613b3582613937565b613b3f8185613af2565b9350613b4a83613953565b8060005b83811015613b7b578151613b628882613b12565b9750613b6d8361398a565b925050600181019050613b4e565b5085935050505092915050565b60006080820190508181036000830152613ba28187613b2a565b90508181036020830152613bb68186613b2a565b9050613bc5604083018561347f565b613bd2606083018461380e565b95945050505050565b600060a082019050613bf06000830188613874565b613bfd6020830187613928565b613c0a6040830186613883565b8181036060830152613c1c8185613997565b9050613c2b6080830184613892565b9695505050505050565b600081519050613c4481613040565b92915050565b600060208284031215613c6057613c5f612edf565b5b6000613c6e84828501613c35565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000613ce082613036565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415613d1357613d12613ca6565b5b600182019050919050565b6000604082019050613d33600083018561347f565b613d40602083018461380e565b9392505050565b6000613d5a613d55846132a8565b61328d565b90508083825260208201905060208402830185811115613d7d57613d7c613076565b5b835b81811015613da65780613d928882613c35565b845260208401935050602081019050613d7f565b5050509392505050565b600082601f830112613dc557613dc461306c565b5b8151613dd5848260208601613d47565b91505092915050565b600060208284031215613df457613df3612edf565b5b600082015167ffffffffffffffff811115613e1257613e11612ee4565b5b613e1e84828501613db0565b91505092915050565b6000606082019050613e3c600083018661347f565b613e49602083018561347f565b613e5660408301846134d6565b949350505050565b6000604082019050613e7360008301856134d6565b613e80602083018461347f565b9392505050565b60008060408385031215613e9e57613e9d612edf565b5b6000613eac85828601613c35565b925050602083015167ffffffffffffffff811115613ecd57613ecc612ee4565b5b613ed985828601613db0565b9150509250929050565b6000613eee8261352c565b9050919050565b613efe81613ee3565b82525050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b613f3981612ff8565b82525050565b6000613f4b8383613f30565b60208301905092915050565b6000602082019050919050565b6000613f6f82613f04565b613f798185613f0f565b9350613f8483613f20565b8060005b83811015613fb5578151613f9c8882613f3f565b9750613fa783613f57565b925050600181019050613f88565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b600082825260208201905092915050565b600061400a82613f04565b6140148185613fee565b935061401f83613f20565b8060005b838110156140505781516140378882613f3f565b975061404283613f57565b925050600181019050614023565b5085935050505092915050565b60006140698383613fff565b905092915050565b6000602082019050919050565b600061408982613fc2565b6140938185613fcd565b9350836020820285016140a585613fde565b8060005b858110156140e157848403895281516140c2858261405d565b94506140cd83614071565b925060208a019950506001810190506140a9565b50829750879550505050505092915050565b600060a0820190506141086000830188613ef5565b818103602083015261411a8187613997565b9050818103604083015261412e8186613f64565b905061413d6060830185613892565b818103608083015261414f818461407e565b90509695505050505050565b600061416682613036565b915061417183613036565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156141a6576141a5613ca6565b5b828201905092915050565b60006040820190506141c6600083018561347f565b6141d3602083018461347f565b9392505050565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60008201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000602082015250565b6000614236603683613a4f565b9150614241826141da565b604082019050919050565b6000602082019050818103600083015261426581614229565b9050919050565b6000604082019050614281600083018561347f565b61428e60208301846134d6565b9392505050565b60006142a082613036565b91506142ab83613036565b9250828210156142be576142bd613ca6565b5b828203905092915050565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b6000614325602a83613a4f565b9150614330826142c9565b604082019050919050565b6000602082019050818103600083015261435481614318565b9050919050565b7f416464726573733a20696e73756666696369656e742062616c616e636520666f60008201527f722063616c6c0000000000000000000000000000000000000000000000000000602082015250565b60006143b7602683613a4f565b91506143c28261435b565b604082019050919050565b600060208201905081810360008301526143e6816143aa565b9050919050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b6000614423601d83613a4f565b915061442e826143ed565b602082019050919050565b6000602082019050818103600083015261445281614416565b9050919050565b600081519050919050565b600081905092915050565b60005b8381101561448d578082015181840152602081019050614472565b8381111561449c576000848401525b50505050565b60006144ad82614459565b6144b78185614464565b93506144c781856020860161446f565b80840191505092915050565b60006144df82846144a2565b915081905092915050565b600081905092915050565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000600082015250565b600061452b6017836144ea565b9150614536826144f5565b601782019050919050565b600081519050919050565b600061455782614541565b61456181856144ea565b935061457181856020860161446f565b80840191505092915050565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000600082015250565b60006145b36011836144ea565b91506145be8261457d565b601182019050919050565b60006145d48261451e565b91506145e0828561454c565b91506145eb826145a6565b91506145f7828461454c565b91508190509392505050565b600061460e82614541565b6146188185613a4f565b935061462881856020860161446f565b6146318161321c565b840191505092915050565b600060208201905081810360008301526146568184614603565b905092915050565b600061466982613036565b915061467483613036565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156146ad576146ac613ca6565b5b828202905092915050565b60006146c382613036565b915060008214156146d7576146d6613ca6565b5b600182039050919050565b7f537472696e67733a20686578206c656e67746820696e73756666696369656e74600082015250565b6000614718602083613a4f565b9150614723826146e2565b602082019050919050565b600060208201905081810360008301526147478161470b565b905091905056fea2646970667358221220ed4846500beed0ab25873bcd8bb4d18d2a76bc4a23c127c9e0b7219ef355058264736f6c634300080a00334a6f6e657353534f5643616c6c445058563353747261746567790000000000000000000000000000000000006c2c06790b3e3e3c38e12ee22f8183b37a13ee55000000000000000000000000db62c01c23e247cea558e90814dbeb215b92c81f000000000000000000000000dc05c718fa4b57c721ea126a68a979bebbb5483e0000000000000000000000007487c19df56b7ea764fc269468b5d3014565052f000000000000000000000000dd0556ddcfe7cdab3540e7f09cb366f498d90774

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

4a6f6e657353534f5643616c6c445058563353747261746567790000000000000000000000000000000000006c2c06790b3e3e3c38e12ee22f8183b37a13ee55000000000000000000000000db62c01c23e247cea558e90814dbeb215b92c81f000000000000000000000000dc05c718fa4b57c721ea126a68a979bebbb5483e0000000000000000000000007487c19df56b7ea764fc269468b5d3014565052f000000000000000000000000dd0556ddcfe7cdab3540e7f09cb366f498d90774

-----Decoded View---------------
Arg [0] : _name (bytes32): 0x4a6f6e657353534f5643616c6c44505856335374726174656779000000000000
Arg [1] : _asset (address): 0x6c2c06790b3e3e3c38e12ee22f8183b37a13ee55
Arg [2] : _SSOV (address): 0xdb62c01c23e247cea558e90814dbeb215b92c81f
Arg [3] : _viewer (address): 0xdc05c718fa4b57c721ea126a68a979bebbb5483e
Arg [4] : _router (address): 0x7487c19df56b7ea764fc269468b5d3014565052f
Arg [5] : _governor (address): 0xdd0556ddcfe7cdab3540e7f09cb366f498d90774

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 4a6f6e657353534f5643616c6c44505856335374726174656779000000000000
Arg [1] : 0000000000000000000000006c2c06790b3e3e3c38e12ee22f8183b37a13ee55
Arg [2] : 000000000000000000000000db62c01c23e247cea558e90814dbeb215b92c81f
Arg [3] : 000000000000000000000000dc05c718fa4b57c721ea126a68a979bebbb5483e
Arg [4] : 0000000000000000000000007487c19df56b7ea764fc269468b5d3014565052f
Arg [5] : 000000000000000000000000dd0556ddcfe7cdab3540e7f09cb366f498d90774


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.