HYPE Price: $25.99 (+4.44%)
 

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Withdraw Fees252057212026-01-22 1:46:045 days ago1769046364IN
0x7AA619b2...08c010a9e
0 HYPE0.000222553.1
0x1eb5ea2e252051022026-01-22 1:35:565 days ago1769045756IN
0x7AA619b2...08c010a9e
0 HYPE0.000434843.1
Custody Funds246768402026-01-16 1:14:2811 days ago1768526068IN
0x7AA619b2...08c010a9e
0 HYPE0.000481223.1
Withdraw Fees231051502025-12-29 3:47:4328 days ago1766980063IN
0x7AA619b2...08c010a9e
0 HYPE0.000222553.1
0x1eb5ea2e231051032025-12-29 3:46:5728 days ago1766980017IN
0x7AA619b2...08c010a9e
0 HYPE0.000434843.1
Custody Funds173805292025-10-24 23:30:3694 days ago1761348636IN
0x7AA619b2...08c010a9e
0 HYPE0.000481223.1
Withdraw Fees173804712025-10-24 23:29:3994 days ago1761348579IN
0x7AA619b2...08c010a9e
0 HYPE0.000222553.1
0x1eb5ea2e169467722025-10-20 0:59:5099 days ago1760921990IN
0x7AA619b2...08c010a9e
0 HYPE0.000447313.19000893
0x2235a68e169462762025-10-20 0:51:4299 days ago1760921502IN
0x7AA619b2...08c010a9e
0 HYPE0.000417992.575
Approve169462632025-10-20 0:51:2999 days ago1760921489IN
0x7AA619b2...08c010a9e
0 HYPE0.000300864.425
Hypurrfi_repay168082992025-10-18 11:09:47100 days ago1760785787IN
0x7AA619b2...08c010a9e
0 HYPE0.000122480.6567
Hypurrfi_borrow168082402025-10-18 11:08:49100 days ago1760785729IN
0x7AA619b2...08c010a9e
0 HYPE0.000432181.30582915
Hypurrfi_supply168082062025-10-18 11:08:15100 days ago1760785695IN
0x7AA619b2...08c010a9e
0 HYPE0.000149450.7773
Hypurrfi_withdra...168081122025-10-18 11:06:43100 days ago1760785603IN
0x7AA619b2...08c010a9e
0 HYPE0.000350541.69314002
Hypurrfi_withdra...168080922025-10-18 11:06:23100 days ago1760785583IN
0x7AA619b2...08c010a9e
0 HYPE0.000366081.711
Hypurrfi_supply168079512025-10-18 11:04:04100 days ago1760785444IN
0x7AA619b2...08c010a9e
0 HYPE0.001812288.592
Hypurrfi_supply168079202025-10-18 11:03:34100 days ago1760785414IN
0x7AA619b2...08c010a9e
0 HYPE0.000445041.97033235
Fix1167278242025-10-17 13:09:19101 days ago1760706559IN
0x7AA619b2...08c010a9e
0 HYPE0.000312673.1
Set Selector165060442025-10-15 0:33:33104 days ago1760488413IN
0x7AA619b2...08c010a9e
0 HYPE0.000157043.21098894
Set Selector165049052025-10-15 0:14:53104 days ago1760487293IN
0x7AA619b2...08c010a9e
0 HYPE0.000161413.30029595
Set Selector165048922025-10-15 0:14:40104 days ago1760487280IN
0x7AA619b2...08c010a9e
0 HYPE0.000246565.04138791
Set Selector165047322025-10-15 0:12:02104 days ago1760487122IN
0x7AA619b2...08c010a9e
0 HYPE0.000192953.94520204
Set Selector165047182025-10-15 0:11:49104 days ago1760487109IN
0x7AA619b2...08c010a9e
0 HYPE0.000205214.19582902
Set Selector165047052025-10-15 0:11:36104 days ago1760487096IN
0x7AA619b2...08c010a9e
0 HYPE0.000217584.44872483
Custody Funds127755412025-09-02 13:09:59146 days ago1756818599IN
0x7AA619b2...08c010a9e
0 HYPE0.000481223.1
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Cross-Chain Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xf070358d...12f7eb9BF
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
Strategy

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion, GNU AGPLv3 license

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import { AccessControl } from "@solidstate/contracts/access/access_control/AccessControl.sol";
import { ITraderV0, TraderV0InitializerParams } from "./modules/Trader.sol";
import { ID2_Module } from "./modules/D2.sol";
import { IWETH_Module } from "./modules/WETH.sol";
import { IInch_Module } from "./modules/Inch.sol";
import { IAave_Module } from "./modules/Aave.sol";
import { IPendle_Module } from "./modules/Pendle.sol";
import { IGMXV2_Module } from "./modules/GMXV2.sol";
import { IDolomite_Module } from "./modules/Dolomite.sol";
import { ISilo_Module } from "./modules/Silo.sol";
import { ICamelot_LP_Module, ICamelot_NFTPool_Module, ICamelot_NitroPool_Module, ICamelot_Swap_Module, ICamelot_V3LP_Module, ICamelot_V3Swap_Module, ICamelot_Storage_Module } from "./modules/Camelot.sol";
import { IBera_Module } from "./modules/Bera.sol";
import { IHype_Module } from "./modules/Hype.sol";
import { IHyperbeat_Module } from "./modules/Hyperbeat.sol";

contract Strategy is AccessControl {
    struct State {
        bool claimed;
        bool frozen;
        mapping(bytes4 => address) selectors;
    }

    bytes32 internal constant STATE_SLOT = keccak256("strategy");

    constructor(address[] memory targets, address[] memory allowedTokens, address[] memory allowedSpenders) {
        State storage s;
        bytes32 slot = STATE_SLOT;
        assembly { s.slot := slot }

        if (block.chainid == 42161) {
            address t = targets[0];
            s.selectors[ITraderV0.setVault.selector] = t;
            s.selectors[ITraderV0.approve.selector] = t;
            s.selectors[ITraderV0.custodyFunds.selector] = t;
            s.selectors[ITraderV0.returnFunds.selector] = t;
            s.selectors[ITraderV0.withdrawFees.selector] = t;
            s.selectors[ITraderV0.setFeeRates.selector] = t;
            s.selectors[ITraderV0.setFeeReceiver.selector] = t;
            s.selectors[ITraderV0.getAllowedTokens.selector] = t;
            s.selectors[ITraderV0.getAllowedSpenders.selector] = t;
            s.selectors[ITraderV0.name.selector] = t;
            s.selectors[ITraderV0.feeReceiver.selector] = t;
            s.selectors[ITraderV0.vault.selector] = t;
            s.selectors[ITraderV0.baseAsset.selector] = t;
            s.selectors[ITraderV0.performanceFeeRate.selector] = t;
            s.selectors[ITraderV0.managementFeeRate.selector] = t;
            s.selectors[ITraderV0.custodyTime.selector] = t;
            s.selectors[ITraderV0.custodiedAmount.selector] = t;
            s.selectors[ITraderV0.totalFees.selector] = t;
            s.selectors[ITraderV0.MAX_PERFORMANCE_FEE_RATE.selector] = t;
            s.selectors[ITraderV0.MAX_MANAGEMENT_FEE_RATE.selector] = t;
            initialize(t, abi.encodeWithSelector(ITraderV0.initializeTraderV0.selector, TraderV0InitializerParams({
                _name: "",
                _allowedTokens: allowedTokens,
                _allowedSpenders: allowedSpenders,
                _initialPerformanceFeeRate: 0.2e18,
                _initialManagementFeeRate: 0.02e18
            })));

            t = targets[1];
            s.selectors[ID2_Module.d2_deposit.selector] = t;
            s.selectors[ID2_Module.d2_withdraw.selector] = t;
            t = targets[2];
            s.selectors[IWETH_Module.weth_deposit.selector] = t;
            s.selectors[IWETH_Module.weth_withdraw.selector] = t;
            t = targets[3];
            s.selectors[IInch_Module.inch_swap.selector] = t;
            s.selectors[IInch_Module.inch_uniswapV3Swap.selector] = t;
            s.selectors[IInch_Module.inch_clipperSwap.selector] = t;
            t = targets[4];
            s.selectors[IAave_Module.aave_supply.selector] = t;
            s.selectors[IAave_Module.aave_withdraw.selector] = t;
            s.selectors[IAave_Module.aave_borrow.selector] = t;
            s.selectors[IAave_Module.aave_repay.selector] = t;
            s.selectors[IAave_Module.aave_setUserEMode.selector] = t;
            s.selectors[IAave_Module.aave_claimRewards.selector] = t;
            t = targets[5];
            s.selectors[IPendle_Module.pendle_deposit.selector] = t;
            s.selectors[IPendle_Module.pendle_withdraw.selector] = t;
            s.selectors[IPendle_Module.pendle_swap.selector] = t;
            s.selectors[IPendle_Module.pendle_claim.selector] = t;
            s.selectors[IPendle_Module.pendle_exit.selector] = t;
            t = targets[6];
            s.selectors[IGMXV2_Module.gmxv2_create.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_update.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_cancel.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_claimFees.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_deposit.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_withdraw.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_glvDeposit.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_glvCancelDeposit.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_glvWithdraw.selector] = t;
            s.selectors[IGMXV2_Module.gmxv2_glvCancelWithdraw.selector] = t;
            t = targets[7];
            s.selectors[IDolomite_Module.dolomite_depositWei.selector] = t;
            s.selectors[IDolomite_Module.dolomite_depositWeiIntoDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawWei.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawWeiFromDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_depositPar.selector] = t;
            s.selectors[IDolomite_Module.dolomite_depositParIntoDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawPar.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawParFromDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_openBorrowPosition.selector] = t;
            s.selectors[IDolomite_Module.dolomite_closeBorrowPosition.selector] = t;
            s.selectors[IDolomite_Module.dolomite_transferBetweenAccounts.selector] = t;
            s.selectors[IDolomite_Module.dolomite_repayAllForBorrowPosition.selector] = t;
            t = targets[8];
            s.selectors[ISilo_Module.silo_deposit.selector] = t;
            s.selectors[ISilo_Module.silo_withdraw.selector] = t;
            s.selectors[ISilo_Module.silo_borrow.selector] = t;
            s.selectors[ISilo_Module.silo_repay.selector] = t;
            s.selectors[ISilo_Module.silo_execute.selector] = t;
            t = targets[9];
            s.selectors[ICamelot_LP_Module.camelot_addLiquidity.selector] = t;
            s.selectors[ICamelot_LP_Module.camelot_addLiquidityETH.selector] = t;
            s.selectors[ICamelot_LP_Module.camelot_removeLiquidity.selector] = t;
            s.selectors[ICamelot_LP_Module.camelot_removeLiquidityETH.selector] = t;
            t = targets[10];
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_createPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_addToPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_harvestPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_withdrawFromPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_renewLockPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_lockPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_splitPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_mergePositions.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.camelot_nftpool_emergencyWithdraw.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.onERC721Received.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.onNFTHarvest.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.onNFTAddToPosition.selector] = t;
            s.selectors[ICamelot_NFTPool_Module.onNFTWithdraw.selector] = t;
            t = targets[11];
            s.selectors[ICamelot_NitroPool_Module.camelot_nitropool_transfer.selector] = t;
            s.selectors[ICamelot_NitroPool_Module.camelot_nitropool_withdraw.selector] = t;
            s.selectors[ICamelot_NitroPool_Module.camelot_nitropool_emergencyWithdraw.selector] = t;
            s.selectors[ICamelot_NitroPool_Module.camelot_nitropool_harvest.selector] = t;
            t = targets[12];
            s.selectors[ICamelot_Swap_Module.camelot_swapExactTokensForTokens.selector] = t;
            s.selectors[ICamelot_Swap_Module.camelot_swapExactETHForTokens.selector] = t;
            s.selectors[ICamelot_Swap_Module.camelot_swapExactTokensForETH.selector] = t;
            t = targets[13];
            s.selectors[ICamelot_V3LP_Module.camelot_v3_mint.selector] = t;
            s.selectors[ICamelot_V3LP_Module.camelot_v3_burn.selector] = t;
            s.selectors[ICamelot_V3LP_Module.camelot_v3_collect.selector] = t;
            s.selectors[ICamelot_V3LP_Module.camelot_v3_increaseLiquidity.selector] = t;
            s.selectors[ICamelot_V3LP_Module.camelot_v3_decreaseLiquidity.selector] = t;
            s.selectors[ICamelot_V3LP_Module.camelot_v3_decreaseLiquidityAndCollect.selector] = t;
            s.selectors[ICamelot_V3LP_Module.camelot_v3_decreaseLiquidityCollectAndBurn.selector] = t;
            t = targets[14];
            s.selectors[ICamelot_V3Swap_Module.camelot_v3_swap.selector] = t;
            t = targets[15];
            s.selectors[ICamelot_Storage_Module.manageNFTPools.selector] = t;
            s.selectors[ICamelot_Storage_Module.manageNitroPools.selector] = t;
            s.selectors[ICamelot_Storage_Module.manageExecutors.selector] = t;
            s.selectors[ICamelot_Storage_Module.manageReceivers.selector] = t;
            s.selectors[ICamelot_Storage_Module.getAllowedNFTPools.selector] = t;
            s.selectors[ICamelot_Storage_Module.getAllowedNitroPools.selector] = t;
            s.selectors[ICamelot_Storage_Module.getAllowedExecutors.selector] = t;
            s.selectors[ICamelot_Storage_Module.getAllowedReceivers.selector] = t;
        } else if (block.chainid == 8453) {
            address t = targets[0];
            s.selectors[ITraderV0.setVault.selector] = t;
            s.selectors[ITraderV0.approve.selector] = t;
            s.selectors[ITraderV0.custodyFunds.selector] = t;
            s.selectors[ITraderV0.returnFunds.selector] = t;
            s.selectors[ITraderV0.withdrawFees.selector] = t;
            s.selectors[ITraderV0.setFeeRates.selector] = t;
            s.selectors[ITraderV0.setFeeReceiver.selector] = t;
            s.selectors[ITraderV0.getAllowedTokens.selector] = t;
            s.selectors[ITraderV0.getAllowedSpenders.selector] = t;
            s.selectors[ITraderV0.name.selector] = t;
            s.selectors[ITraderV0.feeReceiver.selector] = t;
            s.selectors[ITraderV0.vault.selector] = t;
            s.selectors[ITraderV0.baseAsset.selector] = t;
            s.selectors[ITraderV0.performanceFeeRate.selector] = t;
            s.selectors[ITraderV0.managementFeeRate.selector] = t;
            s.selectors[ITraderV0.custodyTime.selector] = t;
            s.selectors[ITraderV0.custodiedAmount.selector] = t;
            s.selectors[ITraderV0.totalFees.selector] = t;
            s.selectors[ITraderV0.MAX_PERFORMANCE_FEE_RATE.selector] = t;
            s.selectors[ITraderV0.MAX_MANAGEMENT_FEE_RATE.selector] = t;
            initialize(t, abi.encodeWithSelector(ITraderV0.initializeTraderV0.selector, TraderV0InitializerParams({
                _name: "",
                _allowedTokens: allowedTokens,
                _allowedSpenders: allowedSpenders,
                _initialPerformanceFeeRate: 0.2e18,
                _initialManagementFeeRate: 0.02e18
            })));
            t = targets[1];
            s.selectors[ID2_Module.d2_deposit.selector] = t;
            s.selectors[ID2_Module.d2_withdraw.selector] = t;
            t = targets[2];
            s.selectors[IWETH_Module.weth_deposit.selector] = t;
            s.selectors[IWETH_Module.weth_withdraw.selector] = t;
            t = targets[3];
            s.selectors[IInch_Module.inch_swap.selector] = t;
            s.selectors[IInch_Module.inch_uniswapV3Swap.selector] = t;
            s.selectors[IInch_Module.inch_clipperSwap.selector] = t;
            t = targets[4];
            s.selectors[IAave_Module.aave_supply.selector] = t;
            s.selectors[IAave_Module.aave_withdraw.selector] = t;
            s.selectors[IAave_Module.aave_borrow.selector] = t;
            s.selectors[IAave_Module.aave_repay.selector] = t;
            s.selectors[IAave_Module.aave_setUserEMode.selector] = t;
            s.selectors[IAave_Module.aave_claimRewards.selector] = t;
            t = targets[5];
            s.selectors[IPendle_Module.pendle_deposit.selector] = t;
            s.selectors[IPendle_Module.pendle_withdraw.selector] = t;
            s.selectors[IPendle_Module.pendle_swap.selector] = t;
            s.selectors[IPendle_Module.pendle_claim.selector] = t;
            s.selectors[IPendle_Module.pendle_exit.selector] = t;
        } else if (block.chainid == 80094) {
            address t = targets[0];
            s.selectors[ITraderV0.setVault.selector] = t;
            s.selectors[ITraderV0.approve.selector] = t;
            s.selectors[ITraderV0.custodyFunds.selector] = t;
            s.selectors[ITraderV0.returnFunds.selector] = t;
            s.selectors[ITraderV0.withdrawFees.selector] = t;
            s.selectors[ITraderV0.setFeeRates.selector] = t;
            s.selectors[ITraderV0.setFeeReceiver.selector] = t;
            s.selectors[ITraderV0.getAllowedTokens.selector] = t;
            s.selectors[ITraderV0.getAllowedSpenders.selector] = t;
            s.selectors[ITraderV0.name.selector] = t;
            s.selectors[ITraderV0.feeReceiver.selector] = t;
            s.selectors[ITraderV0.vault.selector] = t;
            s.selectors[ITraderV0.baseAsset.selector] = t;
            s.selectors[ITraderV0.performanceFeeRate.selector] = t;
            s.selectors[ITraderV0.managementFeeRate.selector] = t;
            s.selectors[ITraderV0.custodyTime.selector] = t;
            s.selectors[ITraderV0.custodiedAmount.selector] = t;
            s.selectors[ITraderV0.totalFees.selector] = t;
            s.selectors[ITraderV0.MAX_PERFORMANCE_FEE_RATE.selector] = t;
            s.selectors[ITraderV0.MAX_MANAGEMENT_FEE_RATE.selector] = t;
            initialize(t, abi.encodeWithSelector(ITraderV0.initializeTraderV0.selector, TraderV0InitializerParams({
                _name: "",
                _allowedTokens: allowedTokens,
                _allowedSpenders: allowedSpenders,
                _initialPerformanceFeeRate: 0.2e18,
                _initialManagementFeeRate: 0.02e18
            })));
            t = targets[1];
            s.selectors[ID2_Module.d2_deposit.selector] = t;
            s.selectors[ID2_Module.d2_withdraw.selector] = t;
            t = targets[2];
            s.selectors[IWETH_Module.weth_deposit.selector] = t;
            s.selectors[IWETH_Module.weth_withdraw.selector] = t;
            t = targets[3];
            s.selectors[IBera_Module.bera_bgt_redeem.selector] = t;
            s.selectors[IBera_Module.bera_bgt_act.selector] = t;
            s.selectors[IBera_Module.bera_bgt_get_reward.selector] = t;
            s.selectors[IBera_Module.bera_vault_stake.selector] = t;
            s.selectors[IBera_Module.bera_vault_withdraw.selector] = t;
            s.selectors[IBera_Module.bera_vault_get_reward.selector] = t;
            s.selectors[IBera_Module.bera_infrared_stake.selector] = t;
            s.selectors[IBera_Module.bera_infrared_withdraw.selector] = t;
            s.selectors[IBera_Module.bera_infrared_get_reward.selector] = t;
            s.selectors[IBera_Module.bera_oogabooga_swap.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv2_add.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv2_remove.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv2_swap.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_mint.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_increase.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_decrease.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_collect.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_burn.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_swap.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_islands_mint.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_islands_burn.selector] = t;
            s.selectors[IBera_Module.bera_kodiakv3_islands_deploy.selector] = t;
            t = targets[4];
            s.selectors[IDolomite_Module.dolomite_depositWei.selector] = t;
            s.selectors[IDolomite_Module.dolomite_depositWeiIntoDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawWei.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawWeiFromDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_depositPar.selector] = t;
            s.selectors[IDolomite_Module.dolomite_depositParIntoDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawPar.selector] = t;
            s.selectors[IDolomite_Module.dolomite_withdrawParFromDefaultAccount.selector] = t;
            s.selectors[IDolomite_Module.dolomite_openBorrowPosition.selector] = t;
            s.selectors[IDolomite_Module.dolomite_closeBorrowPosition.selector] = t;
            s.selectors[IDolomite_Module.dolomite_transferBetweenAccounts.selector] = t;
            s.selectors[IDolomite_Module.dolomite_repayAllForBorrowPosition.selector] = t;
        } else if (block.chainid == 1) {
            address t = targets[0];
            s.selectors[ITraderV0.setVault.selector] = t;
            s.selectors[ITraderV0.approve.selector] = t;
            s.selectors[ITraderV0.custodyFunds.selector] = t;
            s.selectors[ITraderV0.returnFunds.selector] = t;
            s.selectors[ITraderV0.withdrawFees.selector] = t;
            s.selectors[ITraderV0.setFeeRates.selector] = t;
            s.selectors[ITraderV0.setFeeReceiver.selector] = t;
            s.selectors[ITraderV0.getAllowedTokens.selector] = t;
            s.selectors[ITraderV0.getAllowedSpenders.selector] = t;
            s.selectors[ITraderV0.name.selector] = t;
            s.selectors[ITraderV0.feeReceiver.selector] = t;
            s.selectors[ITraderV0.vault.selector] = t;
            s.selectors[ITraderV0.baseAsset.selector] = t;
            s.selectors[ITraderV0.performanceFeeRate.selector] = t;
            s.selectors[ITraderV0.managementFeeRate.selector] = t;
            s.selectors[ITraderV0.custodyTime.selector] = t;
            s.selectors[ITraderV0.custodiedAmount.selector] = t;
            s.selectors[ITraderV0.totalFees.selector] = t;
            s.selectors[ITraderV0.MAX_PERFORMANCE_FEE_RATE.selector] = t;
            s.selectors[ITraderV0.MAX_MANAGEMENT_FEE_RATE.selector] = t;
            initialize(t, abi.encodeWithSelector(ITraderV0.initializeTraderV0.selector, TraderV0InitializerParams({
                _name: "",
                _allowedTokens: allowedTokens,
                _allowedSpenders: allowedSpenders,
                _initialPerformanceFeeRate: 0.2e18,
                _initialManagementFeeRate: 0.02e18
            })));
            t = targets[1];
            s.selectors[ID2_Module.d2_deposit.selector] = t;
            s.selectors[ID2_Module.d2_withdraw.selector] = t;
            t = targets[2];
            s.selectors[IWETH_Module.weth_deposit.selector] = t;
            s.selectors[IWETH_Module.weth_withdraw.selector] = t;
            t = targets[3];
            s.selectors[IInch_Module.inch_swap.selector] = t;
            s.selectors[IInch_Module.inch_uniswapV3Swap.selector] = t;
            s.selectors[IInch_Module.inch_clipperSwap.selector] = t;
            t = targets[4];
            s.selectors[IAave_Module.aave_supply.selector] = t;
            s.selectors[IAave_Module.aave_withdraw.selector] = t;
            s.selectors[IAave_Module.aave_borrow.selector] = t;
            s.selectors[IAave_Module.aave_repay.selector] = t;
            s.selectors[IAave_Module.aave_setUserEMode.selector] = t;
            s.selectors[IAave_Module.aave_claimRewards.selector] = t;
            t = targets[5];
            s.selectors[IPendle_Module.pendle_deposit.selector] = t;
            s.selectors[IPendle_Module.pendle_withdraw.selector] = t;
            s.selectors[IPendle_Module.pendle_swap.selector] = t;
            s.selectors[IPendle_Module.pendle_claim.selector] = t;
            s.selectors[IPendle_Module.pendle_exit.selector] = t;
        } else if (block.chainid == 999 || block.chainid == 998) {
            address t = targets[0];
            s.selectors[ITraderV0.setVault.selector] = t;
            s.selectors[ITraderV0.approve.selector] = t;
            s.selectors[ITraderV0.custodyFunds.selector] = t;
            s.selectors[ITraderV0.returnFunds.selector] = t;
            s.selectors[ITraderV0.withdrawFees.selector] = t;
            s.selectors[ITraderV0.setFeeRates.selector] = t;
            s.selectors[ITraderV0.setFeeReceiver.selector] = t;
            s.selectors[ITraderV0.getAllowedTokens.selector] = t;
            s.selectors[ITraderV0.getAllowedSpenders.selector] = t;
            s.selectors[ITraderV0.name.selector] = t;
            s.selectors[ITraderV0.feeReceiver.selector] = t;
            s.selectors[ITraderV0.vault.selector] = t;
            s.selectors[ITraderV0.baseAsset.selector] = t;
            s.selectors[ITraderV0.performanceFeeRate.selector] = t;
            s.selectors[ITraderV0.managementFeeRate.selector] = t;
            s.selectors[ITraderV0.custodyTime.selector] = t;
            s.selectors[ITraderV0.custodiedAmount.selector] = t;
            s.selectors[ITraderV0.totalFees.selector] = t;
            s.selectors[ITraderV0.MAX_PERFORMANCE_FEE_RATE.selector] = t;
            s.selectors[ITraderV0.MAX_MANAGEMENT_FEE_RATE.selector] = t;
            initialize(t, abi.encodeWithSelector(ITraderV0.initializeTraderV0.selector, TraderV0InitializerParams({
                _name: "",
                _allowedTokens: allowedTokens,
                _allowedSpenders: allowedSpenders,
                _initialPerformanceFeeRate: 0.2e18,
                _initialManagementFeeRate: 0.02e18
            })));
            t = targets[1];
            s.selectors[ID2_Module.d2_deposit.selector] = t;
            s.selectors[ID2_Module.d2_withdraw.selector] = t;
            t = targets[2];
            s.selectors[IHype_Module.hyper_sendIocOrder.selector] = t;
            s.selectors[IHype_Module.hyper_sendVaultTransfer.selector] = t;
            s.selectors[IHype_Module.hyper_sendTokenDelegate.selector] = t;
            s.selectors[IHype_Module.hyper_sendCDeposit.selector] = t;
            s.selectors[IHype_Module.hyper_sendCWithdrawal.selector] = t;
            s.selectors[IHype_Module.hyper_sendSpot.selector] = t;
            s.selectors[IHype_Module.hyper_sendUsdClassTransfer.selector] = t;
            t = targets[3];
            s.selectors[IHyperbeat_Module.hyperbeat_deposit.selector] = t;
            s.selectors[IHyperbeat_Module.hyperbeat_withdraw.selector] = t;
        } else if (block.chainid == 1116) {
            address t = targets[0];
            s.selectors[ITraderV0.setVault.selector] = t;
            s.selectors[ITraderV0.approve.selector] = t;
            s.selectors[ITraderV0.custodyFunds.selector] = t;
            s.selectors[ITraderV0.returnFunds.selector] = t;
            s.selectors[ITraderV0.withdrawFees.selector] = t;
            s.selectors[ITraderV0.setFeeRates.selector] = t;
            s.selectors[ITraderV0.setFeeReceiver.selector] = t;
            s.selectors[ITraderV0.getAllowedTokens.selector] = t;
            s.selectors[ITraderV0.getAllowedSpenders.selector] = t;
            s.selectors[ITraderV0.name.selector] = t;
            s.selectors[ITraderV0.feeReceiver.selector] = t;
            s.selectors[ITraderV0.vault.selector] = t;
            s.selectors[ITraderV0.baseAsset.selector] = t;
            s.selectors[ITraderV0.performanceFeeRate.selector] = t;
            s.selectors[ITraderV0.managementFeeRate.selector] = t;
            s.selectors[ITraderV0.custodyTime.selector] = t;
            s.selectors[ITraderV0.custodiedAmount.selector] = t;
            s.selectors[ITraderV0.totalFees.selector] = t;
            s.selectors[ITraderV0.MAX_PERFORMANCE_FEE_RATE.selector] = t;
            s.selectors[ITraderV0.MAX_MANAGEMENT_FEE_RATE.selector] = t;
            initialize(t, abi.encodeWithSelector(ITraderV0.initializeTraderV0.selector, TraderV0InitializerParams({
                _name: "",
                _allowedTokens: allowedTokens,
                _allowedSpenders: allowedSpenders,
                _initialPerformanceFeeRate: 0.2e18,
                _initialManagementFeeRate: 0.02e18
            })));
            t = targets[1];
            s.selectors[ID2_Module.d2_deposit.selector] = t;
            s.selectors[ID2_Module.d2_withdraw.selector] = t;
        }
    }

    function initialize(address target, bytes memory data) internal {
        (bool success,) = target.delegatecall(data);
        if (!success) {
            assembly {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
    }

    function setFrozen() public onlyRole(0x00) {
        State storage s;
        bytes32 slot = STATE_SLOT;
        assembly { s.slot := slot }
        s.frozen = true;
    }

    function setSelector(bytes4 selector, address target) public onlyRole(0x00) {
        State storage s;
        bytes32 slot = STATE_SLOT;
        assembly { s.slot := slot }
        require(!s.frozen, "frozen");
        s.selectors[selector] = target;
    }

    function claim() public {
        State storage s;
        bytes32 slot = STATE_SLOT;
        assembly { s.slot := slot }
        require(!s.claimed, "claimed");
        s.claimed = true;
        _grantRole(0x00, msg.sender);
        _grantRole(keccak256("EXECUTOR_ROLE"), msg.sender);
    }

    fallback() external payable {
        State storage s;
        bytes32 slot = STATE_SLOT;
        assembly { s.slot := slot }
        address target = s.selectors[msg.sig];
        if (target == address(0)) {
            revert("unknown signature");
        }
        assembly {
            calldatacopy(0, 0, calldatasize())
            let result := delegatecall(gas(), target, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch result
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    receive() external payable {}
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IAccessControl } from './IAccessControl.sol';
import { AccessControlInternal } from './AccessControlInternal.sol';

/**
 * @title Role-based access control system
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
abstract contract AccessControl is IAccessControl, AccessControlInternal {
    /**
     * @inheritdoc IAccessControl
     */
    function grantRole(
        bytes32 role,
        address account
    ) external onlyRole(_getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function hasRole(
        bytes32 role,
        address account
    ) external view returns (bool) {
        return _hasRole(role, account);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32) {
        return _getRoleAdmin(role);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function revokeRole(
        bytes32 role,
        address account
    ) external onlyRole(_getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function renounceRole(bytes32 role) external {
        _renounceRole(role);
    }
}

File 3 of 27 : Trader.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

// contracts/interfaces/IVault.sol

struct Epoch {
    uint256 fundingStart;
    uint256 epochStart;
    uint256 epochEnd;
}

interface IVault {
    function custodyFunds() external returns (uint256);

    function returnFunds(uint256 _amount) external;
    function emergencyEndEpoch(uint256 _amount) external;

    function asset() external returns (address);

    function getCurrentEpochInfo() external view returns (Epoch memory);
}

// contracts/trader/modules/dsq/DSQ_Common_Roles.sol

/**
 * @title   DSquared Common Roles
 * @notice  Access control roles available to all strategy contracts
 * @author  HessianX
 * @custom:developer    BowTiedPickle
 * @custom:developer    BowTiedOriole
 */
abstract contract DSQ_Common_Roles {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
    bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00;
}

// node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol

// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

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

// node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol

// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// node_modules/@openzeppelin/contracts/utils/Address.sol

// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

/**
 * @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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

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

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

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

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

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// node_modules/@openzeppelin/contracts/utils/structs/EnumerableSet.sol

// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

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

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

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

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

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

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

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

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

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

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }
}

// node_modules/@solidstate/contracts/access/access_control/IAccessControlInternal.sol

/**
 * @title Partial AccessControl interface needed by internal functions
 */
interface IAccessControlInternal {
    event RoleAdminChanged(
        bytes32 indexed role,
        bytes32 indexed previousAdminRole,
        bytes32 indexed newAdminRole
    );

    event RoleGranted(
        bytes32 indexed role,
        address indexed account,
        address indexed sender
    );

    event RoleRevoked(
        bytes32 indexed role,
        address indexed account,
        address indexed sender
    );
}

// node_modules/@solidstate/contracts/data/EnumerableSet.sol

/**
 * @title Set implementation with enumeration functions
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
library EnumerableSet_1 {
    error EnumerableSet__IndexOutOfBounds();

    struct Set {
        bytes32[] _values;
        // 1-indexed to allow 0 to signify nonexistence
        mapping(bytes32 => uint256) _indexes;
    }

    struct Bytes32Set {
        Set _inner;
    }

    struct AddressSet {
        Set _inner;
    }

    struct UintSet {
        Set _inner;
    }

    function at(
        Bytes32Set storage set,
        uint256 index
    ) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    function at(
        AddressSet storage set,
        uint256 index
    ) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    function at(
        UintSet storage set,
        uint256 index
    ) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    function contains(
        Bytes32Set storage set,
        bytes32 value
    ) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    function contains(
        AddressSet storage set,
        address value
    ) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    function contains(
        UintSet storage set,
        uint256 value
    ) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    function indexOf(
        Bytes32Set storage set,
        bytes32 value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, value);
    }

    function indexOf(
        AddressSet storage set,
        address value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(uint256(uint160(value))));
    }

    function indexOf(
        UintSet storage set,
        uint256 value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(value));
    }

    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function add(
        Bytes32Set storage set,
        bytes32 value
    ) internal returns (bool) {
        return _add(set._inner, value);
    }

    function add(
        AddressSet storage set,
        address value
    ) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    function remove(
        Bytes32Set storage set,
        bytes32 value
    ) internal returns (bool) {
        return _remove(set._inner, value);
    }

    function remove(
        AddressSet storage set,
        address value
    ) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    function remove(
        UintSet storage set,
        uint256 value
    ) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    function toArray(
        Bytes32Set storage set
    ) internal view returns (bytes32[] memory) {
        return set._inner._values;
    }

    function toArray(
        AddressSet storage set
    ) internal view returns (address[] memory) {
        bytes32[] storage values = set._inner._values;
        address[] storage array;

        assembly {
            array.slot := values.slot
        }

        return array;
    }

    function toArray(
        UintSet storage set
    ) internal view returns (uint256[] memory) {
        bytes32[] storage values = set._inner._values;
        uint256[] storage array;

        assembly {
            array.slot := values.slot
        }

        return array;
    }

    function _at(
        Set storage set,
        uint256 index
    ) private view returns (bytes32) {
        if (index >= set._values.length)
            revert EnumerableSet__IndexOutOfBounds();
        return set._values[index];
    }

    function _contains(
        Set storage set,
        bytes32 value
    ) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    function _indexOf(
        Set storage set,
        bytes32 value
    ) private view returns (uint256) {
        unchecked {
            return set._indexes[value] - 1;
        }
    }

    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    function _add(
        Set storage set,
        bytes32 value
    ) private returns (bool status) {
        if (!_contains(set, value)) {
            set._values.push(value);
            set._indexes[value] = set._values.length;
            status = true;
        }
    }

    function _remove(
        Set storage set,
        bytes32 value
    ) private returns (bool status) {
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            unchecked {
                bytes32 last = set._values[set._values.length - 1];

                // move last value to now-vacant index

                set._values[valueIndex - 1] = last;
                set._indexes[last] = valueIndex;
            }
            // clear last index

            set._values.pop();
            delete set._indexes[value];

            status = true;
        }
    }
}

// node_modules/@solidstate/contracts/utils/UintUtils.sol

/**
 * @title utility functions for uint256 operations
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
 */
library UintUtils {
    error UintUtils__InsufficientHexLength();

    bytes16 private constant HEX_SYMBOLS = '0123456789abcdef';

    function add(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? sub(a, -b) : a + uint256(b);
    }

    function sub(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? add(a, -b) : a - uint256(b);
    }

    function toString(uint256 value) internal pure returns (string memory) {
        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);
    }

    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return '0x00';
        }

        uint256 length = 0;

        for (uint256 temp = value; temp != 0; temp >>= 8) {
            unchecked {
                length++;
            }
        }

        return toHexString(value, 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';

        unchecked {
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
        }

        if (value != 0) revert UintUtils__InsufficientHexLength();

        return string(buffer);
    }
}

// node_modules/@solidstate/contracts/access/access_control/AccessControlStorage.sol

library AccessControlStorage {
    struct RoleData {
        EnumerableSet_1.AddressSet members;
        bytes32 adminRole;
    }

    struct Layout {
        mapping(bytes32 => RoleData) roles;
    }

    bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00;

    bytes32 internal constant STORAGE_SLOT =
        keccak256('solidstate.contracts.storage.AccessControl');

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

// node_modules/@solidstate/contracts/access/access_control/IAccessControl.sol

/**
 * @title AccessControl interface
 */
interface IAccessControl is IAccessControlInternal {
    /*
     * @notice query whether role is assigned to account
     * @param role role to query
     * @param account account to query
     * @return whether role is assigned to account
     */
    function hasRole(
        bytes32 role,
        address account
    ) external view returns (bool);

    /*
     * @notice query admin role for given role
     * @param role role to query
     * @return admin role
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /*
     * @notice assign role to given account
     * @param role role to assign
     * @param account recipient of role assignment
     */
    function grantRole(bytes32 role, address account) external;

    /*
     * @notice unassign role from given account
     * @param role role to unassign
     * @parm account
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @notice relinquish role
     * @param role role to relinquish
     */
    function renounceRole(bytes32 role) external;
}

// node_modules/@solidstate/contracts/utils/AddressUtils.sol

library AddressUtils {
    using UintUtils for uint256;

    error AddressUtils__InsufficientBalance();
    error AddressUtils__NotContract();
    error AddressUtils__SendValueFailed();

    function toString(address account) internal pure returns (string memory) {
        return uint256(uint160(account)).toHexString(20);
    }

    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    function sendValue(address payable account, uint256 amount) internal {
        (bool success, ) = account.call{ value: amount }('');
        if (!success) revert AddressUtils__SendValueFailed();
    }

    function functionCall(
        address target,
        bytes memory data
    ) internal returns (bytes memory) {
        return
            functionCall(target, data, 'AddressUtils: failed low-level call');
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory error
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, error);
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                'AddressUtils: failed low-level call with value'
            );
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) internal returns (bytes memory) {
        if (value > address(this).balance)
            revert AddressUtils__InsufficientBalance();
        return _functionCallWithValue(target, data, value, error);
    }

    /**
     * @notice execute arbitrary external call with limited gas usage and amount of copied return data
     * @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License)
     * @param target recipient of call
     * @param gasAmount gas allowance for call
     * @param value native token value to include in call
     * @param maxCopy maximum number of bytes to copy from return data
     * @param data encoded call data
     * @return success whether call is successful
     * @return returnData copied return data
     */
    function excessivelySafeCall(
        address target,
        uint256 gasAmount,
        uint256 value,
        uint16 maxCopy,
        bytes memory data
    ) internal returns (bool success, bytes memory returnData) {
        returnData = new bytes(maxCopy);

        assembly {
            // execute external call via assembly to avoid automatic copying of return data
            success := call(
                gasAmount,
                target,
                value,
                add(data, 0x20),
                mload(data),
                0,
                0
            )

            // determine whether to limit amount of data to copy
            let toCopy := returndatasize()

            if gt(toCopy, maxCopy) {
                toCopy := maxCopy
            }

            // store the length of the copied bytes
            mstore(returnData, toCopy)

            // copy the bytes from returndata[0:toCopy]
            returndatacopy(add(returnData, 0x20), 0, toCopy)
        }
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) private returns (bytes memory) {
        if (!isContract(target)) revert AddressUtils__NotContract();

        (bool success, bytes memory returnData) = target.call{ value: value }(
            data
        );

        if (success) {
            return returnData;
        } else if (returnData.length > 0) {
            assembly {
                let returnData_size := mload(returnData)
                revert(add(32, returnData), returnData_size)
            }
        } else {
            revert(error);
        }
    }
}

// contracts/trader/trader/ITraderV0.sol

/**
 * @param   _name                       Strategy name
 * @param   _allowedTokens              ERC-20 tokens to include in the strategy's mandate
 * @param   _allowedSpenders            Addresses which can receive ERC-20 token approval
 * @param   _initialPerformanceFeeRate  Initial value of the performance fee, in units of 1e18 = 100%
 * @param   _initialManagementFeeRate   Initial value of the management fee, in units of 1e18 = 100%
 */
struct TraderV0InitializerParams {
    string _name;
    address[] _allowedTokens;
    address[] _allowedSpenders;
    uint256 _initialPerformanceFeeRate;
    uint256 _initialManagementFeeRate;
}

/**
 * @title   DSquared Trader V0 Core Interface
 * @notice  Interfaces with the Vault contract, handling custody, returning, and fee-taking
 * @author  HessianX
 * @custom:developer    BowTiedPickle
 * @custom:developer    BowTiedOriole
 */
interface ITraderV0 {
    // ---------- Construction and Initialization ----------

    function initializeTraderV0(TraderV0InitializerParams calldata _params) external;

    /**
     * @notice  Set this strategy's vault address
     * @dev     May only be set once
     * @param   _vault  Vault address
     */
    function setVault(address _vault, address _asset) external;

    // ---------- Operation ----------

    /**
     * @notice  Approve a whitelisted spender to handle one of the whitelisted tokens
     * @param   _token      Token to set approval for
     * @param   _spender    Spending address
     * @param   _amount     Token amount
     */
    function approve(address _token, address _spender, uint256 _amount) external;

    /**
     * @notice  Take custody of the vault's funds
     * @dev     Relies on Vault contract to revert if called out of sequence.
     */
    function custodyFunds() external;

    /**
     * @notice  Return the vault's funds and take fees if enabled
     * @dev     WARNING: Unwind all positions back into the base asset before returning funds.
     * @dev     Relies on Vault contract to revert if called out of sequence.
     */
    function returnFunds() external;

    /**
     * @notice  Withdraw all accumulated fees
     * @dev     This should be done before the start of the next epoch to avoid fees becoming mixed with vault funds
     */
    function withdrawFees() external;

    // --------- Configuration ----------

    /**
     * @notice  Set new performance and management fees
     * @notice  May not be set while funds are custodied
     * @param   _performanceFeeRate     New management fee (100% = 1e18)
     * @param   _managementFeeRate      New management fee (100% = 1e18)
     */
    function setFeeRates(uint256 _performanceFeeRate, uint256 _managementFeeRate) external;

    /**
     * @notice  Set a new fee receiver address
     * @param   _feeReceiver   Address which will receive fees from the contract
     */
    function setFeeReceiver(address _feeReceiver) external;

    // --------- View Functions ---------

    /**
     * @notice  View all tokens the contract is allowed to handle
     * @return  List of token addresses
     */
    function getAllowedTokens() external view returns (address[] memory);

    /**
     * @notice  View all addresses which can recieve token approvals
     * @return  List of addresses
     */
    function getAllowedSpenders() external view returns (address[] memory);

    // ----- State Variable Getters -----

    /// @notice Strategy name
    function name() external view returns (string memory);

    /// @notice Address receiving fees
    function feeReceiver() external view returns (address);

    /// @notice Vault address
    function vault() external view returns (IVault);

    /// @notice Underlying asset of the strategy's vault
    function baseAsset() external view returns (IERC20);

    /// @notice Performance fee as percentage of profits, in units of 1e18 = 100%
    function performanceFeeRate() external view returns (uint256);

    /// @notice Management fee as percentage of base assets, in units of 1e18 = 100%
    function managementFeeRate() external view returns (uint256);

    /// @notice Timestamp when funds were taken into custody, in Unix epoch seconds
    function custodyTime() external view returns (uint256);

    /// @notice Amount of base asset taken into custody
    function custodiedAmount() external view returns (uint256);

    /// @notice Accumulated management and performance fees
    function totalFees() external view returns (uint256);

    /// @notice Maximum allowable performance fee as percentage of profits, in units of 1e18 = 100%
    function MAX_PERFORMANCE_FEE_RATE() external view returns (uint256);

    /// @notice Maximum allowable management fee as percentage of base assets per year, in units of 1e18 = 100%
    function MAX_MANAGEMENT_FEE_RATE() external view returns (uint256);

    // --------- Hooks ---------

    receive() external payable;

    // ----- Events -----

    event FundsReturned(uint256 startingBalance, uint256 closingBalance, uint256 performanceFee, uint256 managementFee);
    event FeesWithdrawn(address indexed withdrawer, uint256 amount);

    event FeesSet(uint256 oldPerformanceFee, uint256 newPerformanceFee, uint256 oldManagementFee, uint256 newManagementFee);
    event FeeReceiverSet(address oldFeeReceiver, address newFeeReceiver);
}

// contracts/trader/modules/dsq/DSQ_Trader_Storage.sol

/**
 * @title   DSquared DSQ Trader Storage
 * @notice  Contains storage variables and functions common to all traders
 * @dev     Storage follows diamond pattern
 * @author  HessianX
 * @custom:developer    BowTiedPickle
 * @custom:developer    BowTiedOriole
 */
abstract contract DSQ_Trader_Storage {
    using EnumerableSet_0 for EnumerableSet_0.AddressSet;

    struct TraderV0Storage {
        /// @notice Strategy name
        string name;
        /// @notice Address receiving fees
        address feeReceiver;
        /// @notice Vault address
        IVault vault;
        /// @notice Underlying asset of the strategy's vault
        IERC20 baseAsset;
        /// @notice Performance fee as percentage of profits, in units of 1e18 = 100%
        uint256 performanceFeeRate;
        /// @notice Management fee as percentage of base assets, in units of 1e18 = 100%
        uint256 managementFeeRate;
        /// @notice Timestamp when funds were taken into custody, in Unix epoch seconds
        uint256 custodyTime;
        /// @notice Amount of base asset taken into custody
        uint256 custodiedAmount;
        /// @notice Accumulated management and performance fees
        uint256 totalFees;
        /// @notice Tokens which can be held or handled by this contract
        EnumerableSet_0.AddressSet allowedTokens;
        /// @notice Addresses which can be approved by this contract
        EnumerableSet_0.AddressSet allowedSpenders;
        /// @notice Whether the contract has been initialized
        bool initialized;
    }

    /// @dev    EIP-2535 Diamond Storage struct location
    bytes32 internal constant TRADERV0_POSITION = bytes32(uint256(keccak256("TraderV0.storage")) - 1);

    /**
     * @return  storageStruct   TraderV0Storage storage pointer
     */
    function getTraderV0Storage() internal pure returns (TraderV0Storage storage storageStruct) {
        bytes32 position = TRADERV0_POSITION;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            storageStruct.slot := position
        }
    }

    // --------- Internal Functions ---------

    /**
     * @notice  Validates a swap path
     * @param   _path   Array of token addresses to validate
     */
    function validateSwapPath(address[] memory _path) internal view {
        TraderV0Storage storage s = getTraderV0Storage();
        uint256 len = _path.length;
        for (uint256 i; i < len; ) {
            require(s.allowedTokens.contains(_path[i]), "Invalid swap path");
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice  Validates a single token address
     * @param   _token  Address of token to validate
     */
    function validateToken(address _token) internal view {
        require(getTraderV0Storage().allowedTokens.contains(_token), "Invalid token");
    }
}

// node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(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));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(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");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

// node_modules/@solidstate/contracts/access/access_control/AccessControlInternal.sol

/**
 * @title Role-based access control system
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
abstract contract AccessControlInternal is IAccessControlInternal {
    using AddressUtils for address;
    using EnumerableSet_1 for EnumerableSet_1.AddressSet;
    using UintUtils for uint256;

    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /*
     * @notice query whether role is assigned to account
     * @param role role to query
     * @param account account to query
     * @return whether role is assigned to account
     */
    function _hasRole(
        bytes32 role,
        address account
    ) internal view virtual returns (bool) {
        return
            AccessControlStorage.layout().roles[role].members.contains(account);
    }

    /**
     * @notice revert if sender does not have given role
     * @param role role to query
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, msg.sender);
    }

    /**
     * @notice revert if given account does not have given role
     * @param role role to query
     * @param account to query
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!_hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        'AccessControl: account ',
                        account.toString(),
                        ' is missing role ',
                        uint256(role).toHexString(32)
                    )
                )
            );
        }
    }

    /*
     * @notice query admin role for given role
     * @param role role to query
     * @return admin role
     */
    function _getRoleAdmin(
        bytes32 role
    ) internal view virtual returns (bytes32) {
        return AccessControlStorage.layout().roles[role].adminRole;
    }

    /**
     * @notice set role as admin role
     * @param role role to set
     * @param adminRole admin role to set
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = _getRoleAdmin(role);
        AccessControlStorage.layout().roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /*
     * @notice assign role to given account
     * @param role role to assign
     * @param account recipient of role assignment
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        AccessControlStorage.layout().roles[role].members.add(account);
        emit RoleGranted(role, account, msg.sender);
    }

    /*
     * @notice unassign role from given account
     * @param role role to unassign
     * @parm account
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        AccessControlStorage.layout().roles[role].members.remove(account);
        emit RoleRevoked(role, account, msg.sender);
    }

    /**
     * @notice relinquish role
     * @param role role to relinquish
     */
    function _renounceRole(bytes32 role) internal virtual {
        _revokeRole(role, msg.sender);
    }
}

// node_modules/@solidstate/contracts/access/access_control/AccessControl.sol

/**
 * @title Role-based access control system
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
abstract contract AccessControl is IAccessControl, AccessControlInternal {
    /**
     * @inheritdoc IAccessControl
     */
    function grantRole(
        bytes32 role,
        address account
    ) external onlyRole(_getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function hasRole(
        bytes32 role,
        address account
    ) external view returns (bool) {
        return _hasRole(role, account);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32) {
        return _getRoleAdmin(role);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function revokeRole(
        bytes32 role,
        address account
    ) external onlyRole(_getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @inheritdoc IAccessControl
     */
    function renounceRole(bytes32 role) external {
        _renounceRole(role);
    }
}

// contracts/trader/trader/TraderV0.sol

/**
 * @title   DSquared Trader V0 Core
 * @notice  Interfaces with the Vault contract, handling custody, returning, and fee-taking
 * @dev     Warning: This contract is intended for use as a facet of diamond proxy contracts.
 *          Calling it directly may produce unintended or undesirable results.
 * @author  HessianX
 * @custom:developer    BowTiedPickle
 * @custom:developer    BowTiedOriole
 */
contract TraderV0 is ITraderV0, AccessControl, DSQ_Common_Roles, DSQ_Trader_Storage {
    using SafeERC20 for IERC20;
    using EnumerableSet_0 for EnumerableSet_0.AddressSet;

    /// @dev Unit denominator of fee rates
    uint256 public constant FEE_DENOMINATOR = 1e18;
    /// @notice Maximum allowable performance fee as percentage of profits, in units of 1e18 = 100%
    uint256 public immutable MAX_PERFORMANCE_FEE_RATE; // solhint-disable-line var-name-mixedcase
    /// @notice Maximum allowable management fee as percentage of base assets per year, in units of 1e18 = 100%
    uint256 public immutable MAX_MANAGEMENT_FEE_RATE; // solhint-disable-line var-name-mixedcase

    // ---------- Construction and Initialization ----------

    /**
     * @param    _MAX_PERFORMANCE_FEE_RATE   Maximum performance fee as percentage of profits (100% = 1e18)
     * @param    _MAX_MANAGEMENT_FEE_RATE    Maximum management fee as percentage of assets (100% = 1e18)
     */
    // solhint-disable-next-line var-name-mixedcase
    constructor(uint256 _MAX_PERFORMANCE_FEE_RATE, uint256 _MAX_MANAGEMENT_FEE_RATE) {
        MAX_PERFORMANCE_FEE_RATE = _MAX_PERFORMANCE_FEE_RATE;
        MAX_MANAGEMENT_FEE_RATE = _MAX_MANAGEMENT_FEE_RATE;
    }

    /**
     * @notice  Initialize the trader parameters
     * @dev     Should ONLY be called through cut_TraderV0.
     *          Adding this function selector to the Diamond will result in a CRITICAL vulnerabiilty.
     * @param   _params     Initialization parameters
     */
    function initializeTraderV0(TraderV0InitializerParams calldata _params) external {
        TraderV0Storage storage s = getTraderV0Storage();
        require(!s.initialized, "TraderV0: Initializer");
        s.initialized = true;

        s.name = _params._name;
        uint256 len = _params._allowedTokens.length;
        require(len > 0, "!tokens");
        for (uint256 i; i < len; ++i) {
            s.allowedTokens.add(_params._allowedTokens[i]);
        }

        len = _params._allowedSpenders.length;
        require(len > 0, "!spenders");
        for (uint256 i; i < len; ++i) {
            s.allowedSpenders.add(_params._allowedSpenders[i]);
        }

        require(
            MAX_PERFORMANCE_FEE_RATE >= _params._initialPerformanceFeeRate && MAX_MANAGEMENT_FEE_RATE >= _params._initialManagementFeeRate,
            "!rates"
        );
        s.performanceFeeRate = _params._initialPerformanceFeeRate;
        s.managementFeeRate = _params._initialManagementFeeRate;
    }

    /**
     * @notice  Set this strategy's vault address
     * @dev     May only be set once
     * @param   _vault  Vault address
     */
    function setVault(address _vault, address _asset) external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        TraderV0Storage storage s = getTraderV0Storage();
        require(_vault != address(0), "!vault");
        require(address(s.vault) == address(0), "!set");
        s.baseAsset = IERC20(_asset);
        s.vault = IVault(_vault);
    }

    // ---------- Operation ----------

    /**
     * @notice  Approve a whitelisted spender to handle one of the whitelisted tokens
     * @param   _token      Token to set approval for
     * @param   _spender    Spending address
     * @param   _amount     Token amount
     */
    function approve(address _token, address _spender, uint256 _amount) external virtual onlyRole(EXECUTOR_ROLE) {
        TraderV0Storage storage s = getTraderV0Storage();
        require(s.allowedTokens.contains(_token), "!token");
        require(s.allowedSpenders.contains(_spender), "!spender");
        IERC20(_token).approve(_spender, _amount);
    }

    /**
     * @notice  Take custody of the vault's funds
     * @dev     Relies on Vault contract to revert if called out of sequence.
     */
    function custodyFunds() external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        TraderV0Storage storage s = getTraderV0Storage();
        require(s.totalFees == 0, "!fees");
        s.custodyTime = block.timestamp;
        s.custodiedAmount = s.vault.custodyFunds();
    }

    /**
     * @notice  Return the vault's funds and take fees if enabled
     * @dev     WARNING: Unwind all positions back into the base asset before returning funds.
     * @dev     Relies on Vault contract to revert if called out of sequence.
     */
    function returnFunds() external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        TraderV0Storage storage s = getTraderV0Storage();
        uint256 balance = s.baseAsset.balanceOf(address(this));
        uint256 cachedCustodiedAmount = s.custodiedAmount;
        uint256 profit = balance > cachedCustodiedAmount ? balance - cachedCustodiedAmount : 0;
        uint256 performanceFee = (profit * s.performanceFeeRate) / FEE_DENOMINATOR;
        uint256 managementFee = (cachedCustodiedAmount * s.managementFeeRate * (block.timestamp - s.custodyTime)) /
            365 days /
            FEE_DENOMINATOR;

        // If fees exceed balance, take no fees
        if (performanceFee + managementFee > balance) {
            performanceFee = 0;
            managementFee = 0;
        }

        s.totalFees = s.totalFees + performanceFee + managementFee;
        s.custodiedAmount = 0;
        s.custodyTime = 0;

        s.baseAsset.approve(address(s.vault), balance - performanceFee - managementFee);
        s.vault.emergencyEndEpoch(balance - performanceFee - managementFee);

        emit FundsReturned(cachedCustodiedAmount, balance, performanceFee, managementFee);
    }

    /**
     * @notice  Withdraw all accumulated fees
     * @dev     This should be done before the start of the next epoch to avoid fees becoming mixed with vault funds
     */
    function withdrawFees() external virtual onlyRole(EXECUTOR_ROLE) {
        TraderV0Storage storage s = getTraderV0Storage();
        require(s.totalFees > 0, "!fees");
        uint256 amount = s.totalFees;
        s.totalFees = 0;
        s.baseAsset.safeTransfer(s.feeReceiver, amount);
        emit FeesWithdrawn(msg.sender, amount);
    }

    // --------- Configuration ----------

    /**
     * @notice  Set new performance and management fees
     * @notice  May not be set while funds are custodied
     * @param   _performanceFeeRate     New management fee (100% = 1e18)
     * @param   _managementFeeRate      New management fee (100% = 1e18)
     */
    function setFeeRates(uint256 _performanceFeeRate, uint256 _managementFeeRate) external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        TraderV0Storage storage s = getTraderV0Storage();
        require(_performanceFeeRate <= MAX_PERFORMANCE_FEE_RATE && _managementFeeRate <= MAX_MANAGEMENT_FEE_RATE, "!rates");
        require(s.custodyTime == 0, "Custodied");
        emit FeesSet(s.performanceFeeRate, _performanceFeeRate, s.managementFeeRate, _managementFeeRate);
        s.performanceFeeRate = _performanceFeeRate;
        s.managementFeeRate = _managementFeeRate;
    }

    /**
     * @notice  Set a new fee receiver address
     * @param   _feeReceiver   Address which will receive fees from the contract
     */
    function setFeeReceiver(address _feeReceiver) external virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        require(_feeReceiver != address(0), "!zeroAddress");
        TraderV0Storage storage s = getTraderV0Storage();
        emit FeeReceiverSet(s.feeReceiver, _feeReceiver);
        s.feeReceiver = _feeReceiver;
    }

    // --------- View Functions ---------

    /**
     * @notice  View all tokens the contract is allowed to handle
     * @return  List of token addresses
     */
    function getAllowedTokens() external view returns (address[] memory) {
        return getTraderV0Storage().allowedTokens.values();
    }

    /**
     * @notice  View all addresses which can recieve token approvals
     * @return  List of addresses
     */
    function getAllowedSpenders() external view returns (address[] memory) {
        return getTraderV0Storage().allowedSpenders.values();
    }

    // ----- State Variable Getters -----

    /// @notice Strategy name
    function name() external view returns (string memory) {
        return getTraderV0Storage().name;
    }

    /// @notice Address receiving fees
    function feeReceiver() external view returns (address) {
        return getTraderV0Storage().feeReceiver;
    }

    /// @notice Vault address
    function vault() external view returns (IVault) {
        return getTraderV0Storage().vault;
    }

    /// @notice Underlying asset of the strategy's vault
    function baseAsset() external view returns (IERC20) {
        return getTraderV0Storage().baseAsset;
    }

    /// @notice Performance fee as percentage of profits, in units of 1e18 = 100%
    function performanceFeeRate() external view returns (uint256) {
        return getTraderV0Storage().performanceFeeRate;
    }

    /// @notice Management fee as percentage of base assets, in units of 1e18 = 100%
    function managementFeeRate() external view returns (uint256) {
        return getTraderV0Storage().managementFeeRate;
    }

    /// @notice Timestamp when funds were taken into custody, in Unix epoch seconds
    function custodyTime() external view returns (uint256) {
        return getTraderV0Storage().custodyTime;
    }

    /// @notice Amount of base asset taken into custody
    function custodiedAmount() external view returns (uint256) {
        return getTraderV0Storage().custodiedAmount;
    }

    /// @notice Accumulated management and performance fees
    function totalFees() external view returns (uint256) {
        return getTraderV0Storage().totalFees;
    }

    // --------- Hooks ---------

    // solhint-disable-next-line no-empty-blocks
    receive() external payable virtual {}
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";

interface ID2_Module {
    function d2_deposit(address vault, uint256 assets) external;
    function d2_withdraw(address vault, uint256 assets) external;
}

contract D2_Module is ID2_Module, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    function d2_deposit(address vault, uint256 assets) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        ID2Vault(vault).deposit(assets, address(this));
    }

    function d2_withdraw(address vault, uint256 assets) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        ID2Vault(vault).withdraw(assets, address(this), address(this));
    }
}

interface ID2Vault {
    function asset() external view returns (address);
    function deposit(uint256 assets, address receiver) external;
    function withdraw(uint256 assets, address receiver, address owner) external;
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";

interface IWETH_Module {
    function weth_deposit(uint256 amount) external;
    function weth_withdraw(uint256 amount) external;
}

contract WETH_Module is IWETH_Module, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    IWETH public immutable weth;

    constructor(address _weth) {
        weth = IWETH(_weth);
    }

    function weth_deposit(uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        weth.deposit{value: amount}();
    }

    function weth_withdraw(uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        weth.withdraw(amount);
    }
}

interface IWETH {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

interface IInch_Module {
    function inch_swap(
        uint256 valueIn,
        address executor,
        IInch.SwapDescription calldata desc,
        bytes calldata permit,
        bytes calldata data
    ) external returns (uint256 returnAmount, uint256 spentAmount);

    function inch_uniswapV3Swap(uint256 valueIn, uint256 amount, uint256 minReturn, uint256[] calldata pools)
        external
        returns (uint256 returnAmount);

    function inch_clipperSwap(
        uint256 valueIn,
        address clipperExchange,
        address srcToken,
        address dstToken,
        uint256 inputAmount,
        uint256 outputAmount,
        uint256 goodUntil,
        bytes32 r,
        bytes32 vs
    ) external returns (uint256 returnAmount);
}

contract Inch_Module is IInch_Module, AccessControl, ReentrancyGuard {
    using EnumerableSet for EnumerableSet.AddressSet;

    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
    uint256 private constant _ONE_FOR_ZERO_MASK = 1 << 255;

    IInch public immutable inch;
    address public immutable clipper;

    constructor(address _inch, address _clipper) {
        inch = IInch(_inch);
        clipper = _clipper;
    }

    function inch_swap(
        uint256 valueIn,
        address executor,
        IInch.SwapDescription calldata desc,
        bytes calldata permit,
        bytes calldata data
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant returns (uint256 returnAmount, uint256 spentAmount) {
        require(desc.dstReceiver == address(this), "receiver");
        validateToken(address(desc.srcToken));
        validateToken(address(desc.dstToken));
        (returnAmount, spentAmount) = inch.swap{value: valueIn}(executor, desc, permit, data);
    }

    function inch_uniswapV3Swap(uint256 valueIn, uint256 amount, uint256 minReturn, uint256[] calldata pools)
        external
        onlyRole(EXECUTOR_ROLE)
        nonReentrant
        returns (uint256 returnAmount)
    {
        uint256 len = pools.length;
        if (len == 1) {
            IPool pool = IPool(address(uint160(pools[0])));
            validateToken(pool.token0());
            validateToken(pool.token1());
        } else {
            // Validate start token
            bool zeroForOne = pools[0] & _ONE_FOR_ZERO_MASK == 0;
            IPool pool = IPool(address(uint160(pools[0])));
            zeroForOne ? validateToken(pool.token0()) : validateToken(pool.token1());

            // Validate end token
            zeroForOne = pools[len - 1] & _ONE_FOR_ZERO_MASK == 0;
            pool = IPool(address(uint160(pools[len - 1])));
            zeroForOne ? validateToken(pool.token1()) : validateToken(pool.token0());
        }
        returnAmount = inch.uniswapV3Swap{value: valueIn}(amount, minReturn, pools);
    }

    function inch_clipperSwap(
        uint256 valueIn,
        address _clipper,
        address srcToken,
        address dstToken,
        uint256 inputAmount,
        uint256 outputAmount,
        uint256 goodUntil,
        bytes32 r,
        bytes32 vs
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant returns (uint256 returnAmount) {
        require(_clipper == clipper, "invalid clipper exchange");
        if (address(srcToken) != address(0)) {
            validateToken(address(srcToken));
        }
        validateToken(address(dstToken));
        returnAmount = inch.clipperSwap{value: valueIn}(
            _clipper, srcToken, dstToken, inputAmount, outputAmount, goodUntil, r, vs
        );
    }

    struct TraderV0Storage {
        string name;
        address feeReceiver;
        address vault;
        address baseAsset;
        uint256 performanceFeeRate;
        uint256 managementFeeRate;
        uint256 custodyTime;
        uint256 custodiedAmount;
        uint256 totalFees;
        EnumerableSet.AddressSet allowedTokens;
        EnumerableSet.AddressSet allowedSpenders;
        bool initialized;
    }

    bytes32 internal constant TRADERV0_POSITION = bytes32(uint256(keccak256("TraderV0.storage")) - 1);

    function getTraderV0Storage() internal pure returns (TraderV0Storage storage storageStruct) {
        bytes32 position = TRADERV0_POSITION;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            storageStruct.slot := position
        }
    }

    function validateSwapPath(address[] memory _path) internal view {
        TraderV0Storage storage s = getTraderV0Storage();
        uint256 len = _path.length;
        for (uint256 i; i < len;) {
            require(s.allowedTokens.contains(_path[i]), "Invalid swap path");
            unchecked {
                ++i;
            }
        }
    }

    function validateToken(address _token) internal view {
        require(getTraderV0Storage().allowedTokens.contains(_token), "Invalid token");
    }
}

interface IPool {
    function token0() external view returns (address);
    function token1() external view returns (address);
}

interface IInch {
    struct SwapDescription {
        address srcToken;
        address dstToken;
        address payable srcReceiver;
        address payable dstReceiver;
        uint256 amount;
        uint256 minReturnAmount;
        uint256 flags;
    }

    function swap(address executor, SwapDescription calldata desc, bytes calldata permit, bytes calldata data)
        external
        payable
        returns (uint256 returnAmount, uint256 spentAmount);

    function uniswapV3Swap(uint256 amount, uint256 minReturn, uint256[] calldata pools)
        external
        payable
        returns (uint256 returnAmount);

    function clipperSwap(
        address clipperExchange,
        address srcToken,
        address dstToken,
        uint256 inputAmount,
        uint256 outputAmount,
        uint256 goodUntil,
        bytes32 r,
        bytes32 vs
    ) external payable returns (uint256 returnAmount);
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/access/access_control/AccessControl.sol";
import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "./ModuleHelper.sol";

interface IAave_Module {
    function aave_supply(address asset, uint256 amount) external;
    function aave_withdraw(address asset, uint256 amount) external;
    function aave_borrow(address asset, uint256 amount, uint256 interestRateMode) external;
    function aave_repay(address asset, uint256 amount, uint256 interestRateMode) external;
    function aave_setUserEMode(uint8 categoryId) external;
    function aave_claimRewards(address[] calldata assets, address reward) external;
}

contract Aave_Module is IAave_Module, ModuleHelper, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
    uint256 public constant MAX_LTV_FACTOR = 0.8e18;
    uint256 internal constant MANTISSA_FACTOR = 1e18;
    uint256 internal constant BASIS_FACTOR = 1e4;

    IPool public immutable pool;
    IRewardsController public immutable rewardsController;

    constructor(address _pool, address _rewardsController) {
        pool = IPool(_pool);
        rewardsController = IRewardsController(_rewardsController);
    }

    function aave_supply(address asset, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(asset);
        IERC20(asset).approve(address(pool), amount);
        pool.supply(asset, amount, address(this), 0);
    }

    function aave_withdraw(address asset, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        pool.withdraw(asset, amount, address(this));
    }

    function aave_borrow(
        address asset,
        uint256 amount,
        uint256 interestRateMode
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(asset);
        pool.borrow(asset, amount, interestRateMode, 0, address(this));
        (uint256 totalCollateralBase, uint256 totalDebtBase, , , uint256 ltv, ) = pool.getUserAccountData(address(this));
        uint256 maxDebtBase = (totalCollateralBase * ltv * MAX_LTV_FACTOR) / (BASIS_FACTOR * MANTISSA_FACTOR);
        require(totalDebtBase <= maxDebtBase, "borrow amount exceeds max LTV");
    }

    function aave_repay(
        address asset,
        uint256 amount,
        uint256 interestRateMode
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IERC20(asset).approve(address(pool), amount);
        pool.repay(asset, amount, interestRateMode, address(this));
    }

    function aave_setUserEMode(uint8 categoryId) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        pool.setUserEMode(categoryId);
    }

    function aave_claimRewards(address[] calldata assets, address reward) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        rewardsController.claimRewards(
            assets, type(uint256).max, address(this), reward
        );
    }
}

interface IPool {
    function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
    function withdraw(address asset, uint256 amount, address to) external returns (uint256);
    function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external;
    function repay(address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf) external returns (uint256);
    function swapBorrowRateMode(address asset, uint256 interestRateMode) external;
    function getUserAccountData(
        address user
    )
        external
        view
        returns (
            uint256 totalCollateralBase,
            uint256 totalDebtBase,
            uint256 availableBorrowsBase,
            uint256 currentLiquidationThreshold,
            uint256 ltv,
            uint256 healthFactor
        );
    function setUserEMode(uint8 categoryId) external;
}

interface IRewardsController {
    function claimRewards(
        address[] calldata assets,
        uint256 amount,
        address to,
        address reward
    ) external;
}

interface IERC20 {
    function approve(address target, uint256 amount) external;
}

File 8 of 27 : Pendle.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";

interface IPendle_Module {
    function pendle_deposit(address market, uint256 amount, uint256 minAmountOut) external;
    function pendle_withdraw(address market, uint256 amount, uint256 minAmountOut) external;
    function pendle_swap(uint256 kind, address market, uint256 amount, uint256 minAmountOut) external;
    function pendle_claim(address yt, uint256 minAmountOut) external;
    function pendle_exit(address market, uint256 minAmountOut) external;
}

contract Pendle_Module is IPendle_Module, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    IPRouter public immutable router;

    error UnkownSwapKind();

    constructor(address _router) {
        require(_router != address(0), "Pendle_Module: Zero address");
        router = IPRouter(_router);
    }

    function marketToken(address market) internal view returns (address) {
        (address sy,,) = IPMarket(market).readTokens();
        return IPSY(sy).getTokensOut()[0];
    }

    function pendle_deposit(address market, uint256 amount, uint256 minAmountOut) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        address ast = marketToken(market);
        IPRouter.ApproxParams memory approxParams = IPRouter.ApproxParams({
            guessMin: 0,
            guessMax: type(uint256).max,
            guessOffchain: 0,
            maxIteration: 256,
            eps: 1e14 // Maximum 0.01% unused
        });
        IPSwapAggregator.SwapData memory swapData = IPSwapAggregator.SwapData({
            swapType: IPSwapAggregator.SwapType.NONE,
            extRouter: address(0),
            extCalldata: "",
            needScale: false
        });
        IPRouter.TokenInput memory input = IPRouter.TokenInput({
            tokenIn: ast,
            netTokenIn: amount,
            tokenMintSy: ast,
            pendleSwap: address(0),
            swapData: swapData
        });
        IPRouter.LimitOrderData memory limitOrderData = IPRouter.LimitOrderData({
            limitRouter: address(0),
            epsSkipMarket: 0,
            normalFills: new IPRouter.FillOrderParams[](0),
            flashFills: new IPRouter.FillOrderParams[](0),
            optData: ""
        });

        IERC20(ast).approve(address(router), amount);
        router.addLiquiditySingleToken(
            address(this),
            address(market),
            minAmountOut,
            approxParams,
            input,
            limitOrderData
        );
    }

    function pendle_withdraw(address market, uint256 amount, uint256 minAmountOut) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        address ast = marketToken(market);
        IPSwapAggregator.SwapData memory swapData = IPSwapAggregator.SwapData({
            swapType: IPSwapAggregator.SwapType.NONE,
            extRouter: address(0),
            extCalldata: "",
            needScale: false
        });
        IPRouter.TokenOutput memory output = IPRouter.TokenOutput({
            tokenOut: ast,
            minTokenOut: minAmountOut,
            tokenRedeemSy: ast,
            pendleSwap: address(0),
            swapData: swapData
        });
        IPRouter.LimitOrderData memory limitOrderData = IPRouter.LimitOrderData({
            limitRouter: address(0),
            epsSkipMarket: 0,
            normalFills: new IPRouter.FillOrderParams[](0),
            flashFills: new IPRouter.FillOrderParams[](0),
            optData: ""
        });

        IERC20(market).approve(address(router), amount);
        router.removeLiquiditySingleToken(
            address(this),
            address(market),
            amount,
            output,
            limitOrderData
        );
    }

    function pendle_swap(uint256 kind, address market, uint256 amount, uint256 minAmountOut) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        address asset = marketToken(market);
        IPRouter.ApproxParams memory approxParams = IPRouter.ApproxParams({
            guessMin: 0,
            guessMax: type(uint256).max,
            guessOffchain: 0,
            maxIteration: 256,
            eps: 1e14 // Maximum 0.01% unused
        });
        IPRouter.LimitOrderData memory limitOrderData = IPRouter.LimitOrderData({
            limitRouter: address(0),
            epsSkipMarket: 0,
            normalFills: new IPRouter.FillOrderParams[](0),
            flashFills: new IPRouter.FillOrderParams[](0),
            optData: ""
        });
        IPSwapAggregator.SwapData memory swapData = IPSwapAggregator.SwapData({
            swapType: IPSwapAggregator.SwapType.NONE,
            extRouter: address(0),
            extCalldata: "",
            needScale: false
        });
        IPRouter.TokenInput memory input = IPRouter.TokenInput({
            tokenIn: asset,
            netTokenIn: amount,
            tokenMintSy: asset,
            pendleSwap: address(0),
            swapData: swapData
        });
        IPRouter.TokenOutput memory output = IPRouter.TokenOutput({
            tokenOut: asset,
            minTokenOut: minAmountOut,
            tokenRedeemSy: asset,
            pendleSwap: address(0),
            swapData: swapData
        });
        if (kind == 1) {
            IERC20(asset).approve(address(router), amount);
            router.swapExactTokenForPt(
                address(this),
                market,
                minAmountOut,
                approxParams,
                input,
                limitOrderData
            );
        } else if (kind == 2) {
            (, address pt, ) = IPMarket(market).readTokens();
            IERC20(pt).approve(address(router), amount);
            router.swapExactPtForToken(
                address(this),
                market,
                amount,
                output,
                limitOrderData
            );
        } else if (kind == 3) {
            IERC20(asset).approve(address(router), amount);
            router.swapExactTokenForYt(
                address(this),
                market,
                minAmountOut,
                approxParams,
                input,
                limitOrderData
            );
        } else if (kind == 4) {
            (, , address yt) = IPMarket(market).readTokens();
            IERC20(yt).approve(address(router), amount);
            router.swapExactYtForToken(
                address(this),
                market,
                amount,
                output,
                limitOrderData
            );
        } else {
            revert UnkownSwapKind();
        }
    }

    function pendle_claim(address market, uint256 minAmountOut) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        (address sy, , address yt) = IPMarket(market).readTokens();
        IPYT(yt).redeemDueInterestAndRewards(address(this), true, true);
        IPSY(sy).redeem(
            address(this),
            IERC20(sy).balanceOf(address(this)),
            IPSY(sy).getTokensOut()[0],
            minAmountOut,
            false
        );
    }

    function pendle_exit(address market, uint256 minAmountOut) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        address asset = marketToken(market);
        (, address pt, ) = IPMarket(market).readTokens();
        uint256 amount = IERC20(pt).balanceOf(address(this));
        uint256 amountLp = IERC20(market).balanceOf(address(this));
        IERC20(pt).approve(address(router), amount);
        IERC20(market).approve(address(router), amountLp);
        router.exitPostExpToToken(
            address(this),
            market,
            amount,
            amountLp,
            IPRouter.TokenOutput({
                tokenOut: asset,
                minTokenOut: minAmountOut,
                tokenRedeemSy: asset,
                pendleSwap: address(0),
                swapData: IPSwapAggregator.SwapData({
                    swapType: IPSwapAggregator.SwapType.NONE,
                    extRouter: address(0),
                    extCalldata: "",
                    needScale: false
                })
            })
        );
    }
}

interface IERC20 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint256);
    function balanceOf(address) external view returns (uint256);
    function allowance(address, address) external view returns (uint256);
    function approve(address, uint256) external returns (bool);
    function transfer(address, uint256) external returns (bool);
    function transferFrom(address, address, uint256) external returns (bool);
}

interface IPSwapAggregator {
    enum SwapType {
        NONE,
        KYBERSWAP,
        ONE_INCH,
        ETH_WETH
    }
    struct SwapData {
        SwapType swapType;
        address extRouter;
        bytes extCalldata;
        bool needScale;
    }
}

interface IPMarket is IERC20 {
    function redeemRewards(address user) external returns (uint256[] memory);

    function getRewardTokens() external view returns (address[] memory);

    function readTokens() external view returns (address, address, address);
}

interface IPRouter {
    struct TokenInput {
        address tokenIn;
        uint256 netTokenIn;
        address tokenMintSy;
        address pendleSwap;
        IPSwapAggregator.SwapData swapData;
    }

    struct TokenOutput {
        address tokenOut;
        uint256 minTokenOut;
        address tokenRedeemSy;
        address pendleSwap;
        IPSwapAggregator.SwapData swapData;
    }

    struct ApproxParams {
        uint256 guessMin;
        uint256 guessMax;
        uint256 guessOffchain;
        uint256 maxIteration;
        uint256 eps;
    }

    enum OrderType {
        SY_FOR_PT,
        PT_FOR_SY,
        SY_FOR_YT,
        YT_FOR_SY
    }

    struct Order {
        uint256 salt;
        uint256 expiry;
        uint256 nonce;
        OrderType orderType;
        address token;
        address YT;
        address maker;
        address receiver;
        uint256 makingAmount;
        uint256 lnImpliedRate;
        uint256 failSafeRate;
        bytes permit;
    }

    struct FillOrderParams {
        Order order;
        bytes signature;
        uint256 makingAmount;
    }

    struct LimitOrderData {
        address limitRouter;
        uint256 epsSkipMarket; // only used for swap operations, will be ignored otherwise
        FillOrderParams[] normalFills;
        FillOrderParams[] flashFills;
        bytes optData;
    }

    function addLiquiditySingleToken(
        address receiver,
        address market,
        uint256 minLpOut,
        ApproxParams calldata guessPtReceivedFromSy,
        TokenInput calldata input,
        LimitOrderData calldata limit
    ) external payable returns (uint256 netLpOut, uint256 netSyFee, uint256 netSyInterm);

    function removeLiquiditySingleToken(
        address receiver,
        address market,
        uint256 netLpToRemove,
        TokenOutput calldata output,
        LimitOrderData calldata limit
    ) external returns (uint256 netTokenOut, uint256 netSyFee, uint256 netSyInterm);

    function exitPostExpToToken(
        address receiver,
        address market,
        uint256 netPtIn,
        uint256 netLpIn,
        TokenOutput calldata output
    ) external;

    function swapExactTokenForPt(
        address receiver,
        address market,
        uint256 minPtOut,
        ApproxParams calldata guessPtOut,
        TokenInput calldata input,
        LimitOrderData calldata limit
    ) external payable returns (uint256 netPtOut, uint256 netSyFee, uint256 netSyInterm);

    function swapExactPtForToken(
        address receiver,
        address market,
        uint256 exactPtIn,
        TokenOutput calldata output,
        LimitOrderData calldata limit
    ) external returns (uint256 netTokenOut, uint256 netSyFee, uint256 netSyInterm);

    function swapExactTokenForYt(
        address receiver,
        address market,
        uint256 minYtOut,
        ApproxParams calldata guessYtOut,
        TokenInput calldata input,
        LimitOrderData calldata limit
    ) external payable returns (uint256 netYtOut, uint256 netSyFee, uint256 netSyInterm);

    function swapExactYtForToken(
        address receiver,
        address market,
        uint256 exactYtIn,
        TokenOutput calldata output,
        LimitOrderData calldata limit
    ) external returns (uint256 netTokenOut, uint256 netSyFee, uint256 netSyInterm);
}

interface IPSY {
    function getTokensOut() external view returns (address[] memory);

    function redeem(
        address receiver,
        uint256 amountSharesToRedeem,
        address tokenOut,
        uint256 minTokenOut,
        bool burnFromInternalBalance
    ) external returns (uint256 amountTokenOut);
}

interface IPYT {
    function SY() external view returns (address);

    function redeemDueInterestAndRewards(
        address user,
        bool redeemInterest,
        bool redeemRewards
    ) external returns (uint256 interestOut, uint256[] memory rewardsOut);
}

File 9 of 27 : GMXV2.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";

interface IGMXV2_Module {
    function gmxv2_create(
        address market,
        address collateral,
        address[] calldata path,
        IExchangeRouter.OrderType orderType,
        IExchangeRouter.DecreasePositionSwapType decreasePositionSwapType,
        bool isLong,
        uint256 sizeDeltaUsd,
        uint256 initialCollateralDeltaAmount,
        uint256 triggerPrice,
        uint256 acceptablePrice,
        uint256 minOutputAmount,
        uint256 executionFee
    ) external payable;
    function gmxv2_update(
        bytes32 key,
        uint256 sizeDeltaUsd,
        uint256 acceptablePrice,
        uint256 triggerPrice,
        uint256 minOutputAmount
    ) external;
    function gmxv2_cancel(bytes32 key) external;
    function gmxv2_claimFees(address[] memory markets, address[] memory tokens) external;
    function gmxv2_deposit(address market, uint256 executionFee, uint256 short, uint256 long, uint256 minOut)
        external
        payable;
    function gmxv2_withdraw(
        address market,
        uint256 executionFee,
        uint256 amount,
        uint256 minLongOut,
        uint256 minShortOut
    ) external payable;
    function gmxv2_glvDeposit(address glv, address market, uint256 longAmount, uint256 shortAmount, uint256 executionFee) external payable;
    function gmxv2_glvWithdraw(address glv, address market, uint256 amount, uint256 executionFee) external payable;
    function gmxv2_glvCancelDeposit(bytes32 key) external payable;
    function gmxv2_glvCancelWithdraw(bytes32 key) external payable;
}

/*
contract GMXV2_Module is IGMXV2_Module, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    address public immutable dataStore;
    address public immutable reader;
    IExchangeRouter public immutable exchangeRouter;
    address public immutable orderVault;
    address public immutable depositVault;
    address public immutable withdrawVault;
    IGlvRouter public immutable glvRouter;

    constructor(
        address _dataStore,
        address _reader,
        address _exchangeRouter,
        address _orderVault,
        address _depositVault,
        address _withdrawVault,
        address _glvRouter
    ) {
        dataStore = _dataStore;
        reader = _reader;
        exchangeRouter = IExchangeRouter(_exchangeRouter);
        orderVault = _orderVault;
        depositVault = _depositVault;
        withdrawVault = _withdrawVault;
        glvRouter = IGlvRouter(_glvRouter);
    }

    function gmxv2_create(
        address market,
        address collateral,
        address[] calldata path,
        IExchangeRouter.OrderType orderType,
        IExchangeRouter.DecreasePositionSwapType decreasePositionSwapType,
        bool isLong,
        uint256 sizeDeltaUsd,
        uint256 initialCollateralDeltaAmount,
        uint256 triggerPrice,
        uint256 acceptablePrice,
        uint256 minOutputAmount,
        uint256 executionFee
    ) external payable onlyRole(EXECUTOR_ROLE) nonReentrant {
        IExchangeRouter.CreateOrderParams memory params = IExchangeRouter.CreateOrderParams({
            addresses: IExchangeRouter.CreateOrderParamsAddresses({
                receiver: address(this),
                cancellationReceiver: address(this),
                callbackContract: address(0),
                uiFeeReceiver: address(0),
                market: market,
                initialCollateralToken: collateral,
                swapPath: path
            }),
            numbers: IExchangeRouter.CreateOrderParamsNumbers({
                sizeDeltaUsd: sizeDeltaUsd,
                initialCollateralDeltaAmount: initialCollateralDeltaAmount,
                triggerPrice: triggerPrice,
                acceptablePrice: acceptablePrice,
                executionFee: executionFee,
                callbackGasLimit: 0,
                minOutputAmount: minOutputAmount,
                validFromTime: 0
            }),
            orderType: orderType,
            decreasePositionSwapType: decreasePositionSwapType,
            isLong: isLong,
            shouldUnwrapNativeToken: false,
            autoCancel: false,
            referralCode: ""
        });
        if (
            orderType == IExchangeRouter.OrderType.LimitSwap || orderType == IExchangeRouter.OrderType.MarketIncrease
                || orderType == IExchangeRouter.OrderType.LimitIncrease
        ) {
            IERC20(collateral).approve(exchangeRouter.router(), initialCollateralDeltaAmount);
            bytes[] memory data = new bytes[](3);
            data[0] = abi.encodeWithSelector(IExchangeRouter.sendWnt.selector, orderVault, executionFee);
            data[1] = abi.encodeWithSelector(
                IExchangeRouter.sendTokens.selector, collateral, orderVault, initialCollateralDeltaAmount
            );
            data[2] = abi.encodeWithSelector(IExchangeRouter.createOrder.selector, params);
            exchangeRouter.multicall{value: executionFee}(data);
        } else {
            bytes[] memory data = new bytes[](2);
            data[0] = abi.encodeWithSelector(IExchangeRouter.sendWnt.selector, orderVault, executionFee);
            data[1] = abi.encodeWithSelector(IExchangeRouter.createOrder.selector, params);
            exchangeRouter.multicall{value: executionFee}(data);
        }
    }

    function gmxv2_update(
        bytes32 key,
        uint256 sizeDeltaUsd,
        uint256 acceptablePrice,
        uint256 triggerPrice,
        uint256 minOutputAmount
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        exchangeRouter.updateOrder(key, sizeDeltaUsd, acceptablePrice, triggerPrice, minOutputAmount, 0, false);
    }

    function gmxv2_cancel(bytes32 key) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        exchangeRouter.cancelOrder(key);
    }

    function gmxv2_claimFees(address[] memory markets, address[] memory tokens)
        external
        onlyRole(EXECUTOR_ROLE)
        nonReentrant
    {
        exchangeRouter.claimFundingFees(markets, tokens, address(this));
    }

    function gmxv2_deposit(address market, uint256 executionFee, uint256 long, uint256 short, uint256 minOut)
        external
        payable
        onlyRole(EXECUTOR_ROLE)
        nonReentrant
    {
        IMarket.Props memory marketInfo = IReader(reader).getMarket(dataStore, market);
        IExchangeRouter.CreateDepositParams memory params = IExchangeRouter.CreateDepositParams({
            receiver: address(this),
            callbackContract: address(0),
            uiFeeReceiver: address(0),
            market: market,
            initialLongToken: marketInfo.longToken,
            initialShortToken: marketInfo.shortToken,
            longTokenSwapPath: new address[](0),
            shortTokenSwapPath: new address[](0),
            minMarketTokens: minOut,
            shouldUnwrapNativeToken: false,
            executionFee: executionFee,
            callbackGasLimit: 0
        });
        bytes[] memory data = new bytes[](4);
        IERC20(marketInfo.longToken).approve(exchangeRouter.router(), long);
        IERC20(marketInfo.shortToken).approve(exchangeRouter.router(), short);
        data[0] = abi.encodeWithSelector(IExchangeRouter.sendWnt.selector, depositVault, params.executionFee);
        data[1] = abi.encodeWithSelector(IExchangeRouter.sendTokens.selector, marketInfo.longToken, depositVault, long);
        data[2] =
            abi.encodeWithSelector(IExchangeRouter.sendTokens.selector, marketInfo.shortToken, depositVault, short);
        data[3] = abi.encodeWithSelector(IExchangeRouter.createDeposit.selector, params);
        exchangeRouter.multicall{value: params.executionFee}(data);
    }

    function gmxv2_withdraw(
        address market,
        uint256 executionFee,
        uint256 amount,
        uint256 minLongOut,
        uint256 minShortOut
    ) external payable onlyRole(EXECUTOR_ROLE) nonReentrant {
        IExchangeRouter.CreateWithdrawalParams memory params = IExchangeRouter.CreateWithdrawalParams({
            receiver: address(this),
            callbackContract: address(0),
            uiFeeReceiver: address(0),
            market: market,
            longTokenSwapPath: new address[](0),
            shortTokenSwapPath: new address[](0),
            minLongTokenAmount: minLongOut,
            minShortTokenAmount: minShortOut,
            shouldUnwrapNativeToken: false,
            executionFee: executionFee,
            callbackGasLimit: 0
        });

        bytes[] memory data = new bytes[](3);
        IERC20(market).approve(exchangeRouter.router(), amount);
        data[0] = abi.encodeWithSelector(IExchangeRouter.sendWnt.selector, withdrawVault, params.executionFee);
        data[1] = abi.encodeWithSelector(IExchangeRouter.sendTokens.selector, market, withdrawVault, amount);
        data[2] = abi.encodeWithSelector(IExchangeRouter.createWithdrawal.selector, params);
        exchangeRouter.multicall{value: params.executionFee}(data);
    }

    function gmxv2_glvDeposit(address glv, address market, uint256 longAmount, uint256 shortAmount, uint256 executionFee) external payable onlyRole(EXECUTOR_ROLE) {
        IMarket.Props memory marketInfo = IReader(reader).getMarket(dataStore, market);
        address glvVault = IGlvHandler(glvRouter.glvHandler()).glvVault();
        bytes[] memory data = new bytes[](3);
        data[0] = abi.encodeWithSelector(IExchangeRouter.sendWnt.selector, glvVault, executionFee);
        data[1] = abi.encodeWithSelector(IExchangeRouter.sendTokens.selector, marketInfo.longToken, glvVault, longAmount);
        data[2] = abi.encodeWithSelector(IExchangeRouter.sendTokens.selector, marketInfo.shortToken, glvVault, shortAmount);
        IERC20(marketInfo.longToken).approve(exchangeRouter.router(), longAmount);
        IERC20(marketInfo.shortToken).approve(exchangeRouter.router(), shortAmount);
        exchangeRouter.multicall{value: executionFee}(data);
        glvRouter.createGlvDeposit(IGlvRouter.CreateGlvDepositParams({
            glv: glv, 
            market: market,
            receiver: address(this),
            callbackContract: address(0),
            uiFeeReceiver: address(0),
            initialLongToken: marketInfo.longToken,
            initialShortToken: marketInfo.shortToken,
            longTokenSwapPath: new address[](0),
            shortTokenSwapPath: new address[](0),
            minGlvTokens: 0,
            executionFee: executionFee,
            callbackGasLimit: 0,
            shouldUnwrapNativeToken: false,
            isMarketTokenDeposit: false
        }));
    }

    function gmxv2_glvCancelDeposit(bytes32 key) external payable onlyRole(EXECUTOR_ROLE) nonReentrant {
        glvRouter.cancelGlvDeposit(key);
    }

    function gmxv2_glvWithdraw(address glv, address market, uint256 amount, uint256 executionFee) external payable onlyRole(EXECUTOR_ROLE) {
        address glvVault = IGlvHandler(glvRouter.glvHandler()).glvVault();
        bytes[] memory data = new bytes[](2);
        data[0] = abi.encodeWithSelector(IExchangeRouter.sendWnt.selector, glvVault, executionFee);
        data[1] = abi.encodeWithSelector(IExchangeRouter.sendTokens.selector, glv, glvVault, amount);
        IERC20(glv).approve(exchangeRouter.router(), amount);
        exchangeRouter.multicall{value: executionFee}(data);
        glvRouter.createGlvWithdrawal(IGlvRouter.CreateGlvWithdrawalParams({
            receiver: address(this),
            callbackContract: address(0),
            uiFeeReceiver: address(0),
            market: market,
            glv: glv, 
            longTokenSwapPath: new address[](0),
            shortTokenSwapPath: new address[](0),
            minLongTokenAmount: 0,
            minShortTokenAmount: 0,
            shouldUnwrapNativeToken: false,
            executionFee: executionFee,
            callbackGasLimit: 0
        }));
    }

    function gmxv2_glvCancelWithdraw(bytes32 key) external payable onlyRole(EXECUTOR_ROLE) nonReentrant {
        glvRouter.cancelGlvDeposit(key);
    }
}
*/

interface IERC20 {
    function approve(address spender, uint256 amount) external;
}

interface IExchangeRouter {
    enum OrderType {
        MarketSwap,
        LimitSwap,
        MarketIncrease,
        LimitIncrease,
        MarketDecrease,
        LimitDecrease,
        StopLossDecrease
    }
    enum DecreasePositionSwapType {
        NoSwap,
        SwapPnlTokenToCollateralToken,
        SwapCollateralTokenToPnlToken
    }

    struct CreateOrderParams {
        CreateOrderParamsAddresses addresses;
        CreateOrderParamsNumbers numbers;
        OrderType orderType;
        DecreasePositionSwapType decreasePositionSwapType;
        bool isLong;
        bool shouldUnwrapNativeToken;
        bool autoCancel;
        bytes32 referralCode;
    }

    struct CreateOrderParamsAddresses {
        address receiver;
        address cancellationReceiver;
        address callbackContract;
        address uiFeeReceiver;
        address market;
        address initialCollateralToken;
        address[] swapPath;
    }

    struct CreateOrderParamsNumbers {
        uint256 sizeDeltaUsd;
        uint256 initialCollateralDeltaAmount;
        uint256 triggerPrice;
        uint256 acceptablePrice;
        uint256 executionFee;
        uint256 callbackGasLimit;
        uint256 minOutputAmount;
        uint256 validFromTime;
    }

    struct CreateDepositParams {
        address receiver;
        address callbackContract;
        address uiFeeReceiver;
        address market;
        address initialLongToken;
        address initialShortToken;
        address[] longTokenSwapPath;
        address[] shortTokenSwapPath;
        uint256 minMarketTokens;
        bool shouldUnwrapNativeToken;
        uint256 executionFee;
        uint256 callbackGasLimit;
    }


    struct CreateWithdrawalParams {
        address receiver;
        address callbackContract;
        address uiFeeReceiver;
        address market;
        address[] longTokenSwapPath;
        address[] shortTokenSwapPath;
        uint256 minLongTokenAmount;
        uint256 minShortTokenAmount;
        bool shouldUnwrapNativeToken;
        uint256 executionFee;
        uint256 callbackGasLimit;
    }

    function sendWnt(address receiver, uint256 amount) external payable;
    function sendTokens(address token, address receiver, uint256 amount) external payable;
    function createOrder(CreateOrderParams calldata params) external payable returns (bytes32);
    function updateOrder(
        bytes32 key,
        uint256 sizeDeltaUsd,
        uint256 acceptablePrice,
        uint256 triggerPrice,
        uint256 minOutputAmount,
        uint256 validFromTime,
        bool autoCancel
    ) external payable;
    function cancelOrder(bytes32 key) external payable;
    function claimFundingFees(address[] memory markets, address[] memory tokens, address receiver) external payable;
    function createDeposit(CreateDepositParams calldata params) external payable returns (bytes32);
    function createWithdrawal(CreateWithdrawalParams calldata params) external payable returns (bytes32);
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
    function router() external returns (address);
}

interface IMarket {
    struct Props {
        address marketToken;
        address indexToken;
        address longToken;
        address shortToken;
    }
}

interface IReader {
    function getMarket(address dataStore, address key) external view returns (IMarket.Props memory);
}

interface IGlvRouter {
    function glvHandler() external view returns (address);
    function createGlvDeposit(
        CreateGlvDepositParams calldata params
    ) external payable;
    function cancelGlvDeposit(bytes32 key) external;
    function createGlvWithdrawal(
        CreateGlvWithdrawalParams calldata params
    ) external payable;
    function cancelGlvWithdrawal(bytes32 key) external;
    function makeExternalCalls(
        address[] memory externalCallTargets,
        bytes[] memory externalCallDataList,
        address[] memory refundTokens,
        address[] memory refundReceivers
    ) external;

    struct CreateGlvDepositParams {
        address glv;
        address market;
        address receiver;
        address callbackContract;
        address uiFeeReceiver;
        address initialLongToken;
        address initialShortToken;
        address[] longTokenSwapPath;
        address[] shortTokenSwapPath;
        uint256 minGlvTokens;
        uint256 executionFee;
        uint256 callbackGasLimit;
        bool shouldUnwrapNativeToken;
        bool isMarketTokenDeposit;
    }

    struct CreateGlvWithdrawalParams {
        address receiver;
        address callbackContract;
        address uiFeeReceiver;
        address market;
        address glv;
        address[] longTokenSwapPath;
        address[] shortTokenSwapPath;
        uint256 minLongTokenAmount;
        uint256 minShortTokenAmount;
        bool shouldUnwrapNativeToken;
        uint256 executionFee;
        uint256 callbackGasLimit;
    }
}

interface IGlvHandler {
    function glvVault() external view returns (address);
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";
import "./IDolomite.sol";

interface IDolomite_Module {
    function dolomite_depositWei(
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountWei
    ) external;

    function dolomite_depositWeiIntoDefaultAccount(
        uint256 _marketId,
        uint256 _amountWei
    ) external;

    function dolomite_withdrawWei(
        uint256 _fromAccountNumber,
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    function dolomite_withdrawWeiFromDefaultAccount(
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    function dolomite_depositPar(
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountPar
    ) external;

    function dolomite_depositParIntoDefaultAccount(
        uint256 _marketId,
        uint256 _amountPar
    ) external;

    function dolomite_withdrawPar(
        uint256 _fromAccountNumber,
        uint256 _marketId,
        uint256 _amountPar,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    function dolomite_withdrawParFromDefaultAccount(
        uint256 _marketId,
        uint256 _amountPar,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    function dolomite_openBorrowPosition(
        uint256 _fromAccountNumber,
        uint256 _toAccountNumber,
        uint256 _collateralMarketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    function dolomite_closeBorrowPosition(
        uint256 _borrowAccountNumber,
        uint256 _toAccountNumber,
        uint256[] calldata _collateralMarketIds
    ) external;

    function dolomite_transferBetweenAccounts(
        uint256 _fromAccountNumber,
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    function dolomite_repayAllForBorrowPosition(
        uint256 _fromAccountNumber,
        uint256 _borrowAccountNumber,
        uint256 _marketId,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;
}

contract Dolomite_Module is IDolomite_Module, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    IDepositWithdrawalProxy public immutable depositWithdrawalProxy;
    IBorrowPositionProxyV2 public immutable borrowPositionProxy;

    constructor(address _depositWithdrawalProxy, address _borrowPositionProxy) {
        require(_depositWithdrawalProxy != address(0), "Rodeo_Module: Zero address");
        require(_borrowPositionProxy != address(0), "Rodeo_Module: Zero address");
        depositWithdrawalProxy = IDepositWithdrawalProxy(_depositWithdrawalProxy);
        borrowPositionProxy = IBorrowPositionProxyV2(_borrowPositionProxy);
    }

    function dolomite_depositWei(
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountWei
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.depositWei(_toAccountNumber, _marketId, _amountWei);
    }

    function dolomite_depositWeiIntoDefaultAccount(
        uint256 _marketId,
        uint256 _amountWei
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.depositWeiIntoDefaultAccount(_marketId, _amountWei);
    }

    function dolomite_withdrawWei(
        uint256 _fromAccountNumber,
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.withdrawWei(_fromAccountNumber, _marketId, _amountWei, _balanceCheckFlag);
    }

    function dolomite_withdrawWeiFromDefaultAccount(
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.withdrawWeiFromDefaultAccount(_marketId, _amountWei, _balanceCheckFlag);
    }

    function dolomite_depositPar(
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountPar
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.depositPar(_toAccountNumber, _marketId, _amountPar);
    }

    function dolomite_depositParIntoDefaultAccount(
        uint256 _marketId,
        uint256 _amountPar
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.depositParIntoDefaultAccount(_marketId, _amountPar);
    }

    function dolomite_withdrawPar(
        uint256 _fromAccountNumber,
        uint256 _marketId,
        uint256 _amountPar,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.withdrawPar(_fromAccountNumber, _marketId, _amountPar, _balanceCheckFlag);
    }

    function dolomite_withdrawParFromDefaultAccount(
        uint256 _marketId,
        uint256 _amountPar,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        depositWithdrawalProxy.withdrawParFromDefaultAccount(_marketId, _amountPar, _balanceCheckFlag);
    }

    function dolomite_openBorrowPosition(
        uint256 _fromAccountNumber,
        uint256 _toAccountNumber,
        uint256 _collateralMarketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        borrowPositionProxy.openBorrowPosition(_fromAccountNumber, _toAccountNumber, _collateralMarketId, _amountWei, _balanceCheckFlag);
    }

    function dolomite_closeBorrowPosition(
        uint256 _borrowAccountNumber,
        uint256 _toAccountNumber,
        uint256[] calldata _collateralMarketIds
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        borrowPositionProxy.closeBorrowPosition(_borrowAccountNumber, _toAccountNumber, _collateralMarketIds);
    }

    function dolomite_transferBetweenAccounts(
        uint256 _fromAccountNumber,
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        borrowPositionProxy.transferBetweenAccounts(_fromAccountNumber, _toAccountNumber, _marketId, _amountWei, _balanceCheckFlag);
    }

    function dolomite_repayAllForBorrowPosition(
        uint256 _fromAccountNumber,
        uint256 _borrowAccountNumber,
        uint256 _marketId,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        borrowPositionProxy.repayAllForBorrowPosition(_fromAccountNumber, _borrowAccountNumber, _marketId, _balanceCheckFlag);
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";
import "./ModuleHelper.sol";

interface ISilo_Module {
    function silo_deposit(address, address, uint256, bool) external;
    function silo_withdraw(address, address, uint256, bool) external;
    function silo_borrow(address, address, uint256) external;
    function silo_repay(address, address, uint256) external;
    function silo_execute(ISiloRouter.Action[] calldata) external;
}

contract Silo_Module is ISilo_Module, ModuleHelper, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    ISiloRouter public immutable router;

    constructor(address _router) {
        require(_router != address(0), "Silo_Module: Zero address");
        router = ISiloRouter(_router);
    }

    function getSilo(address silo) internal view returns (ISilo) {
        ISiloRepository r = ISiloRepository(router.siloRepository());
        require(r.isSilo(silo), "Silo_Module: not silo");
        return ISilo(silo);
    }

    function silo_deposit(address silo, address asset, uint256 amount, bool collateralOnly) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(asset);
        IERC20(asset).approve(silo, amount);
        ISilo s = getSilo(silo);
        s.deposit(asset, amount, collateralOnly);
    }

    function silo_withdraw(address silo, address asset, uint256 amount, bool collateralOnly) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(asset);
        ISilo s = getSilo(silo);
        s.withdraw(asset, amount, collateralOnly);
    }

    function silo_borrow(address silo, address asset, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(asset);
        ISilo s = getSilo(silo);
        s.borrow(asset, amount);
    }

    function silo_repay(address silo, address asset, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IERC20(asset).approve(silo, amount);
        ISilo s = getSilo(silo);
        s.repay(asset, amount);
    }

    function silo_execute(ISiloRouter.Action[] calldata actions) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IERC20(actions[0].asset).approve(address(router), actions[0].amount);
        router.execute(actions);
    }
}

interface IERC20 {
    function balanceOf(address) external view returns (uint256);
    function approve(address, uint256) external returns (bool);
    function transfer(address, uint256) external returns (bool);
}

interface ISilo {
    // bool is collateralOnly
    function deposit(address, uint256, bool) external;
    function withdraw(address, uint256, bool) external;
    function borrow(address, uint256) external;
    function repay(address, uint256) external;
}

interface ISiloRouter {
    enum ActionType {
        Deposit,
        Withdraw,
        Borrow,
        Repay
    }
    struct Action {
        ActionType actionType;
        address silo;
        address asset;
        uint256 amount;
        bool collateralOnly;
    }

    function execute(Action[] calldata _actions) external payable;
    function siloRepository() external view returns (address);
}

interface ISiloRepository {
    function isSilo(address) external view returns (bool);
}

File 12 of 27 : Camelot.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

interface ICamelot_LP_Module {
    // ---------- Functions ----------

    function camelot_addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external;

    function camelot_addLiquidityETH(
        uint valueIn,
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external;

    function camelot_removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external;

    function camelot_removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external;

    // ---------- Getters ----------
    function camelot_router() external view returns (address);

    function weth() external view returns (address);
}

interface ICamelot_NFTPool_Module {
    // ---------- Functions ----------

    function camelot_nftpool_createPosition(address _poolAddress, uint256 _amount, uint256 _lockDuration) external;

    function camelot_nftpool_addToPosition(address _poolAddress, uint256 _tokenId, uint256 _amountToAdd) external;

    function camelot_nftpool_harvestPosition(address _poolAddress, uint256 _tokenId) external;

    function camelot_nftpool_withdrawFromPosition(address _poolAddress, uint256 _tokenId, uint256 _amountToWithdraw) external;

    function camelot_nftpool_renewLockPosition(address _poolAddress, uint256 _tokenId) external;

    function camelot_nftpool_lockPosition(address _poolAddress, uint256 _tokenId, uint256 _lockDuration) external;

    function camelot_nftpool_splitPosition(address _poolAddress, uint256 _tokenId, uint256 _splitAmount) external;

    function camelot_nftpool_mergePositions(address _poolAddress, uint256[] calldata _tokenIds, uint256 _lockDuration) external;

    function camelot_nftpool_emergencyWithdraw(address _poolAddress, uint256 _tokenId) external;

    // ---------- Callbacks ----------

    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4);

    function onNFTHarvest(
        address operator,
        address to,
        uint256 tokenId,
        uint256 grailAmount,
        uint256 xGrailAmount
    ) external pure returns (bool);

    function onNFTAddToPosition(address operator, uint256 tokenId, uint256 lpAmount) external pure returns (bool);

    function onNFTWithdraw(address operator, uint256 tokenId, uint256 lpAmount) external pure returns (bool);
}


interface ICamelot_NitroPool_Module {
    // ---------- Functions ----------

    function camelot_nitropool_transfer(address _nitroPoolAddress, address _nftPoolAddress, uint256 _tokenId) external;

    function camelot_nitropool_withdraw(address _poolAddress, uint256 _tokenId) external;

    function camelot_nitropool_emergencyWithdraw(address _poolAddress, uint256 _tokenId) external;

    function camelot_nitropool_harvest(address _poolAddress) external;
}


interface ICamelot_Swap_Module {
    // ---------- Functions ----------

    function camelot_swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        address referrer,
        uint deadline
    ) external;

    function camelot_swapExactETHForTokens(
        uint valueIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        address referrer,
        uint deadline
    ) external;

    function camelot_swapExactTokensForETH(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        address referrer,
        uint deadline
    ) external;

    // ---------- Getters ----------

    function camelot_router() external view returns (address);
}


interface ICamelot_V3LP_Module {
    // ---------- Functions ----------

    function camelot_v3_mint(uint256 valueIn, INonfungiblePositionManager.MintParams calldata params) external returns (uint256);

    function camelot_v3_burn(uint256 tokenId) external;

    function camelot_v3_collect(INonfungiblePositionManager.CollectParams memory params) external;

    function camelot_v3_increaseLiquidity(uint256 valueIn, INonfungiblePositionManager.IncreaseLiquidityParams calldata params) external;

    function camelot_v3_decreaseLiquidity(INonfungiblePositionManager.DecreaseLiquidityParams calldata params) external;

    function camelot_v3_decreaseLiquidityAndCollect(
        INonfungiblePositionManager.DecreaseLiquidityParams calldata decreaseParams,
        INonfungiblePositionManager.CollectParams memory collectParams
    ) external;

    function camelot_v3_decreaseLiquidityCollectAndBurn(
        INonfungiblePositionManager.DecreaseLiquidityParams calldata decreaseParams,
        INonfungiblePositionManager.CollectParams memory collectParams,
        uint256 tokenId
    ) external;
}


interface ICamelot_V3Swap_Module {
    // ---------- Functions ----------

    function camelot_v3_swap(
        uint256 valueIn,
        IOdosRouter.inputToken[] memory inputs,
        IOdosRouter.outputToken[] memory outputs,
        uint256 valueOutQuote,
        uint256 valueOutMin,
        address executor,
        bytes calldata pathDefinition
    ) external;
}


interface ICamelot_Storage_Module {
    // --------- External Functions ---------

    function manageNFTPools(address[] calldata _pools, bool[] calldata _status) external;

    function manageNitroPools(address[] calldata _pools, bool[] calldata _status, uint256[] calldata _indexes) external;

    function manageExecutors(address[] calldata _executors, bool[] calldata _status) external;

    function manageReceivers(address[] calldata _receivers, bool[] calldata _status) external;

    // --------- Getter Functions ---------

    function camelot_nftpool_factory() external view returns (INFTPoolFactory);

    function getAllowedNFTPools() external view returns (address[] memory);

    function getAllowedNitroPools() external view returns (address[] memory);

    function getAllowedExecutors() external view returns (address[] memory);

    function getAllowedReceivers() external view returns (address[] memory);
}

interface INonfungiblePositionManager {
    event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    struct MintParams {
        address token0;
        address token1;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function positions(
        uint256 tokenId
    )
        external
        view
        returns (
            uint88 nonce,
            address operator,
            address token0,
            address token1,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    function multicall(bytes[] calldata data) external;

    function refundNativeToken() external;

    function increaseLiquidity(IncreaseLiquidityParams calldata params) external payable;

    function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    function mint(
        MintParams calldata params
    ) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    function burn(uint256 tokenId) external payable;

    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    function ownerOf(uint256 tokenId) external view returns (address owner);

    function sweepToken(address token, uint256 amountMinimum, address recipient) external;

    function unwrapWNativeToken(uint256 amountMinimum, address recipient) external;
}

interface IOdosRouter {
    /// @dev Contains all information needed to describe an input token being swapped from
    struct inputToken {
        address tokenAddress;
        uint256 amountIn;
        address receiver;
        bytes permit;
    }
    /// @dev Contains all information needed to describe an output token being swapped to
    struct outputToken {
        address tokenAddress;
        uint256 relativeValue;
        address receiver;
    }

    function swap(
        inputToken[] memory inputs,
        outputToken[] memory outputs,
        uint256 valueOutQuote,
        uint256 valueOutMin,
        address executor,
        bytes calldata pathDefinition
    ) external payable returns (uint256[] memory amountsOut, uint256 gasLeft);
}

interface INFTPoolFactory {
    function getPool(address _lpToken) external view returns (address);
}

File 13 of 27 : Bera.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/access/access_control/AccessControl.sol";
import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "./ModuleHelper.sol";

// https://documentation.kodiak.finance/overview/kodiak-contracts

interface IBera_Module {
    function bera_bgt_redeem(uint256 amount) external;
    function bera_bgt_act(uint256 action, bytes calldata target, uint128 amount) external;
    function bera_bgt_get_reward() external;
    function bera_vault_stake(address token, uint256 amount) external;
    function bera_vault_withdraw(address token, uint256 amount) external;
    function bera_vault_get_reward(address token) external;
    function bera_infrared_stake(address vault, address token, uint256 amount) external;
    function bera_infrared_withdraw(address vault, uint256 amount) external;
    function bera_infrared_get_reward(address vault) external;
    function bera_oogabooga_swap(IOogaBooga.SwapTokenInfo memory tokenInfo, bytes calldata pathDefinition, address executor, uint32 referralCode, uint256 value) external;
    function bera_kodiakv2_add(address tokenA, address tokenB, uint256 amountA, uint256 amountB, uint256 amountAMin, uint256 amountBMin) external;
    function bera_kodiakv2_remove(address tokenA, address tokenB, uint256 amount, uint256 amountAMin, uint256 amountBMin) external;
    function bera_kodiakv2_swap(address token, uint amount, uint amountMin, address[] calldata path) external;
    function bera_kodiakv3_mint(address tokenA, address tokenB, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amountA, uint256 amountB, uint256 amountAMin, uint256 amountBMin) external;
    function bera_kodiakv3_increase(address tokenA, address tokenB, uint256 tokenId, uint256 amountA, uint256 amountB, uint256 amountAMin, uint256 amountBMin) external;
    function bera_kodiakv3_decrease(uint256 tokenId, uint256 amount, uint256 amountAMin, uint256 amountBMin) external;
    function bera_kodiakv3_collect(uint256 tokenId) external;
    function bera_kodiakv3_burn(uint256 tokenId) external;
    function bera_kodiakv3_swap(address token, uint amount, uint amountMin, bytes memory path) external;
    function bera_kodiakv3_islands_mint(address island, uint amount) external;
    function bera_kodiakv3_islands_burn(address island, uint amount) external;
    function bera_kodiakv3_islands_deploy(address tokenA, address tokenB, uint24 uniFee, int24 lowerTick, int24 upperTick) external;
}

contract Bera_Module is ModuleHelper, AccessControl, ReentrancyGuard {
    using EnumerableSet for EnumerableSet.AddressSet;

    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    IBGT public immutable bgt;
    IBGTStaker public immutable bgtStaker;
    IVaultFactory public immutable vaultFactory;
    IOogaBooga public immutable oogaBooga;
    IKodiakV2 public immutable kodiakv2;
    IKodiakV3 public immutable kodiakv3;
    IKodiakV3SwapRouter public immutable kodiakv3swap;
    IKodiakV3IslandFactory public immutable kodiakv3islandFactory;

    constructor(address _bgt, address _bgtStaker, address _vaultFactory, address _oogaBooga, address _kodiakv2, address _kodiakv3, address _kodiakv3swap, address _kodiakv3islandFactory) {
        bgt = IBGT(_bgt);
        bgtStaker = IBGTStaker(_bgtStaker);
        vaultFactory = IVaultFactory(_vaultFactory);
        oogaBooga = IOogaBooga(payable(_oogaBooga));
        kodiakv2 = IKodiakV2(_kodiakv2);
        kodiakv3 = IKodiakV3(_kodiakv3);
        kodiakv3swap = IKodiakV3SwapRouter(_kodiakv3swap);
        kodiakv3islandFactory = IKodiakV3IslandFactory(_kodiakv3islandFactory);
    }

    function bera_bgt_redeem(uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        bgt.redeem(address(this), amount);
    }

    function bera_bgt_act(uint256 action, bytes calldata target, uint128 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        if (action == 1) {
            bgt.queueBoost(target, amount);
        } else if (action == 2) {
            bgt.cancelBoost(target, amount);
        } else if (action == 3) {
            bgt.activateBoost(address(this), target);
        } else if (action == 4) {
            bgt.queueDropBoost(target, amount);
        } else if (action == 5) {
            bgt.cancelDropBoost(target, amount);
        } else if (action == 6) {
            bgt.dropBoost(address(this), target);
        }
    }

    function bera_bgt_get_reward() external onlyRole(EXECUTOR_ROLE) nonReentrant {
        bgtStaker.getReward();
    }

    function bera_vault_stake(address token, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IVault(vaultFactory.getVault(token)).stake(amount);
    }

    function bera_vault_withdraw(address token, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IVault(vaultFactory.getVault(token)).withdraw(amount);
    }

    function bera_vault_get_reward(address token) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IVault(vaultFactory.getVault(token)).getReward(address(this), address(this));
    }

    function bera_infrared_stake(address vault, address token, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateSpender(vault);
        IERC20(token).approve(vault, amount);
        IInfraredVault(vault).stake(amount);
    }

    function bera_infrared_withdraw(address vault, uint256 amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IInfraredVault(vault).withdraw(amount);
    }

    function bera_infrared_get_reward(address vault) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IInfraredVault(vault).getReward();
    }

    function bera_oogabooga_swap(IOogaBooga.SwapTokenInfo memory tokenInfo, bytes calldata pathDefinition, address executor, uint32 referralCode, uint256 value) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        require(tokenInfo.outputReceiver == address(this), "receiver");
        validateToken(tokenInfo.inputToken);
        validateToken(tokenInfo.outputToken);
        IERC20(tokenInfo.inputToken).approve(address(oogaBooga), tokenInfo.inputAmount);
        oogaBooga.swap{value: value}(tokenInfo, pathDefinition, executor, referralCode);
    }

    function bera_kodiakv2_add(address tokenA, address tokenB, uint256 amountA, uint256 amountB, uint256 amountAMin, uint256 amountBMin) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(tokenA);
        validateToken(tokenB);
        IERC20(tokenA).approve(address(kodiakv2), amountA);
        IERC20(tokenB).approve(address(kodiakv2), amountB);
        kodiakv2.addLiquidity(tokenA, tokenB, amountA, amountB, amountAMin, amountBMin, address(this), type(uint256).max);
    }

    function bera_kodiakv2_remove(address tokenA, address tokenB, uint256 amount, uint256 amountAMin, uint256 amountBMin) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        address pair = IKodiakV2(kodiakv2.factory()).getPair(tokenA, tokenB);
        IERC20(pair).approve(address(kodiakv2), amount);
        kodiakv2.removeLiquidity(tokenA, tokenB, amount, amountAMin, amountBMin, address(this), type(uint256).max);
    }

    function bera_kodiakv2_swap(address token, uint amount, uint amountMin, address[] calldata path) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(token);
        validateToken(path[path.length - 1]);
        IERC20(token).approve(address(kodiakv2), amount);
        kodiakv2.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            amount,
            amountMin,
            path,
            address(this),
            type(uint256).max
        );
    }

    function bera_kodiakv3_mint(address tokenA, address tokenB, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amountA, uint256 amountB, uint256 amountAMin, uint256 amountBMin) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(tokenA);
        validateToken(tokenB);
        IERC20(tokenA).approve(address(kodiakv3), amountA);
        IERC20(tokenB).approve(address(kodiakv3), amountB);
        kodiakv3.mint(IKodiakV3.MintParams({
            token0: tokenA,
            token1: tokenB,
            fee: fee,
            tickLower: tickLower,
            tickUpper: tickUpper,
            amount0Desired: amountA,
            amount1Desired: amountB,
            amount0Min: amountAMin,
            amount1Min: amountBMin,
            recipient: address(this),
            deadline: type(uint256).max
        }));
    }

    function bera_kodiakv3_increase(address tokenA, address tokenB, uint256 tokenId, uint256 amountA, uint256 amountB, uint256 amountAMin, uint256 amountBMin) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IERC20(tokenA).approve(address(kodiakv3), amountA);
        IERC20(tokenB).approve(address(kodiakv3), amountB);
        kodiakv3.increaseLiquidity(IKodiakV3.IncreaseLiquidityParams({
            tokenId: tokenId,
            amount0Desired: amountA,
            amount1Desired: amountB,
            amount0Min: amountAMin,
            amount1Min: amountBMin,
            deadline: type(uint256).max
        }));
    }

    function bera_kodiakv3_decrease(uint256 tokenId, uint256 amount, uint256 amountAMin, uint256 amountBMin) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        kodiakv3.decreaseLiquidity(IKodiakV3.DecreaseLiquidityParams({
            tokenId: tokenId,
            liquidity: uint128(amount),
            amount0Min: amountAMin,
            amount1Min: amountBMin,
            deadline: type(uint256).max
        }));
    }

    function bera_kodiakv3_collect(uint256 tokenId) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        kodiakv3.collect(IKodiakV3.CollectParams({
            tokenId: tokenId,
            recipient: address(this),
            amount0Max: type(uint128).max,
            amount1Max: type(uint128).max
        }));
    }

    function bera_kodiakv3_burn(uint256 tokenId) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        kodiakv3.burn(tokenId);
    }

    function bera_kodiakv3_swap(address token, uint amount, uint amountMin, bytes calldata path) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateToken(token);
        validateToken(pathLastAddress(path));
        IERC20(token).approve(address(kodiakv3swap), amount);
        kodiakv3swap.exactInput(IKodiakV3SwapRouter.ExactInputParams({
            path: path,
            recipient: address(this),
            amountIn: amount,
            amountOutMinimum: amountMin
        }));
    }

    function bera_kodiakv3_islands_mint(address island, uint amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateSpender(island);
        IERC20(IKodiakV3Island(island).token0()).approve(island, type(uint256).max);
        IERC20(IKodiakV3Island(island).token1()).approve(island, type(uint256).max);
        IKodiakV3Island(island).mint(amount, address(this));
    }

    function bera_kodiakv3_islands_burn(address island, uint amount) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        validateSpender(island);
        IKodiakV3Island(island).burn(amount, address(this));
    }

    function bera_kodiakv3_islands_deploy(address tokenA, address tokenB, uint24 uniFee, int24 lowerTick, int24 upperTick) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        address island = kodiakv3islandFactory.deployVault(
            tokenA, tokenB, uniFee,
            address(0), address(0), 0,
            lowerTick, upperTick
        );
        getTraderV0Storage().allowedSpenders.add(island);
    }

    function pathLastAddress(bytes memory path) internal pure returns (address) {
        require(path.length >= 20, "pathLastAddress");
        address output;
        assembly {
            output := shr(96, mload(add(path, add(0x20, sub(mload(path), 20)))))
        }
        return output;
    }
}

interface IERC20 {
    function approve(address target, uint256 amount) external;
}

interface IBGT {
    function redeem(address receiver, uint256 amount) external;
    function queueBoost(bytes calldata pubkey, uint128 amount) external;
    function cancelBoost(bytes calldata pubkey, uint128 amount) external;
    function activateBoost(address user, bytes calldata pubkey) external;
    function queueDropBoost(bytes calldata pubkey, uint128 amount) external;
    function cancelDropBoost(bytes calldata pubkey, uint128 amount) external;
    function dropBoost(address user, bytes calldata pubkey) external returns (bool);
}

interface IBGTStaker {
    function getReward() external;
}

interface IVaultFactory {
  function getVault(address stakingToken) external view returns (address);
}

interface IVault {
  function stake(uint256 amount) external;
  function withdraw(uint256 amount) external;
  function getReward(address account, address recipient) external;
}

interface IInfraredVault {
  function stake(uint256 amount) external;
  function withdraw(uint256 amount) external;
  function getReward() external;
}

interface IOogaBooga {
    struct SwapTokenInfo {
        address inputToken;
        uint256 inputAmount;
        address outputToken;
        uint256 outputQuote;
        uint256 outputMin;
        address outputReceiver;
    }
    function swap(SwapTokenInfo memory tokenInfo, bytes calldata pathDefinition, address executor, uint32 referralCode) external payable;
}

interface IKodiakV2 {
    function factory() external view returns (address);
    function getPair(address, address) external view returns (address);
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external;
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external;
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] memory path,
        address to,
        uint deadline
    ) external;
}

interface IKodiakV3 {
    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }
    function mint(MintParams calldata params)
        external
        payable
        returns (
            uint256 tokenId,
            uint128 liquidity,
            uint256 amount0,
            uint256 amount1
        );
    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }
    function increaseLiquidity(IncreaseLiquidityParams calldata params)
        external
        payable
        returns (
            uint128 liquidity,
            uint256 amount0,
            uint256 amount1
        );
    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }
    function decreaseLiquidity(DecreaseLiquidityParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);
    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }
    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
    function burn(uint256 tokenId) external payable;
}

interface IKodiakV3SwapRouter {
    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }
    function exactInput(ExactInputParams memory params) external;
}

interface IKodiakV3Island {
    function token0() external view returns (address);
    function token1() external view returns (address);
    function mint(uint256 mintAmount, address receiver) external;
    function burn(uint256 burnAmount, address receiver) external;
}

interface IKodiakV3IslandFactory {
    function deployVault(
        address tokenA,
        address tokenB,
        uint24 uniFee,
        address manager,
        address managerTreasury,
        uint16 managerFee,
        int24 lowerTick,
        int24 upperTick
    ) external returns (address);
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/access/access_control/AccessControl.sol";
import "@solidstate/contracts/utils/ReentrancyGuard.sol";

interface IHype_Module {
    function hyper_sendIocOrder(uint32 asset, bool isBuy, uint64 limitPx, uint64 sz) external;
    function hyper_sendVaultTransfer(address vault, bool isDeposit, uint64 usd) external;
    function hyper_sendTokenDelegate(address validator, uint64 _wei, bool isUndelegate) external;
    function hyper_sendCDeposit(uint64 _wei) external;
    function hyper_sendCWithdrawal(uint64 _wei) external;
    function hyper_sendSpot(uint64 token, uint64 _wei) external;
    function hyper_sendUsdClassTransfer(uint64 ntl, bool toPerp) external;
}

contract Hype_Module is IHype_Module, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
    Core public immutable core = Core(0x3333333333333333333333333333333333333333);

    function hyper_sendIocOrder(
        uint32 asset,
        bool isBuy,
        uint64 limitPx,
        uint64 sz
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        core.sendIocOrder(asset, isBuy, limitPx, sz);
    }

    function hyper_sendVaultTransfer(
        address vault,
        bool isDeposit,
        uint64 usd
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        core.sendVaultTransfer(vault, isDeposit, usd);
    }

    function hyper_sendTokenDelegate(
        address validator,
        uint64 _wei,
        bool isUndelegate
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        core.sendTokenDelegate(validator, _wei, isUndelegate);
    }

    function hyper_sendCDeposit(uint64 _wei) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        core.sendCDeposit(_wei);
    }

    function hyper_sendCWithdrawal(uint64 _wei) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        core.sendCWithdrawal(_wei);
    }

    function hyper_sendSpot(
        uint64 token,
        uint64 _wei
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        core.sendSpot(address(this), token, _wei);
    }

    function hyper_sendUsdClassTransfer(
        uint64 ntl,
        bool toPerp
    ) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        core.sendUsdClassTransfer(ntl, toPerp);
    }
}

interface Core {
    function sendIocOrder(uint32 asset, bool isBuy, uint64 limitPx, uint64 sz) external;
    function sendVaultTransfer(address vault, bool isDeposit, uint64 usd) external;
    function sendTokenDelegate(address validator, uint64 _wei, bool isUndelegate) external;
    function sendCDeposit(uint64 _wei) external;
    function sendCWithdrawal(uint64 _wei) external;
    function sendSpot(address destination, uint64 token, uint64 _wei) external;
    function sendUsdClassTransfer(uint64 ntl, bool toPerp) external;
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@solidstate/contracts/utils/ReentrancyGuard.sol";
import "@solidstate/contracts/access/access_control/AccessControl.sol";

interface IHyperbeat_Module {
    function hyperbeat_deposit(address vault, uint256 assets) external;
    function hyperbeat_withdraw(address vault, uint256 assets) external;
}

contract Hyperbeat_Module is IHyperbeat_Module, AccessControl, ReentrancyGuard {
    bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");

    function hyperbeat_deposit(address vault, uint256 assets) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IHyperbeatVault(vault).deposit(assets, address(this));
    }

    function hyperbeat_withdraw(address vault, uint256 assets) external onlyRole(EXECUTOR_ROLE) nonReentrant {
        IHyperbeatVault(vault).requestRedeem(assets, address(this), address(this));
    }
}

interface IHyperbeatVault {
    function asset() external view returns (address);
    function deposit(uint256 assets, address receiver) external;
    function requestRedeem(uint256 assets, address receiver, address owner) external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IAccessControlInternal } from './IAccessControlInternal.sol';

/**
 * @title AccessControl interface
 */
interface IAccessControl is IAccessControlInternal {
    /*
     * @notice query whether role is assigned to account
     * @param role role to query
     * @param account account to query
     * @return whether role is assigned to account
     */
    function hasRole(
        bytes32 role,
        address account
    ) external view returns (bool);

    /*
     * @notice query admin role for given role
     * @param role role to query
     * @return admin role
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /*
     * @notice assign role to given account
     * @param role role to assign
     * @param account recipient of role assignment
     */
    function grantRole(bytes32 role, address account) external;

    /*
     * @notice unassign role from given account
     * @param role role to unassign
     * @parm account
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @notice relinquish role
     * @param role role to relinquish
     */
    function renounceRole(bytes32 role) external;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { EnumerableSet } from '../../data/EnumerableSet.sol';
import { AddressUtils } from '../../utils/AddressUtils.sol';
import { UintUtils } from '../../utils/UintUtils.sol';
import { IAccessControlInternal } from './IAccessControlInternal.sol';
import { AccessControlStorage } from './AccessControlStorage.sol';

/**
 * @title Role-based access control system
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
abstract contract AccessControlInternal is IAccessControlInternal {
    using AddressUtils for address;
    using EnumerableSet for EnumerableSet.AddressSet;
    using UintUtils for uint256;

    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /*
     * @notice query whether role is assigned to account
     * @param role role to query
     * @param account account to query
     * @return whether role is assigned to account
     */
    function _hasRole(
        bytes32 role,
        address account
    ) internal view virtual returns (bool) {
        return
            AccessControlStorage.layout().roles[role].members.contains(account);
    }

    /**
     * @notice revert if sender does not have given role
     * @param role role to query
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, msg.sender);
    }

    /**
     * @notice revert if given account does not have given role
     * @param role role to query
     * @param account to query
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!_hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        'AccessControl: account ',
                        account.toString(),
                        ' is missing role ',
                        uint256(role).toHexString(32)
                    )
                )
            );
        }
    }

    /*
     * @notice query admin role for given role
     * @param role role to query
     * @return admin role
     */
    function _getRoleAdmin(
        bytes32 role
    ) internal view virtual returns (bytes32) {
        return AccessControlStorage.layout().roles[role].adminRole;
    }

    /**
     * @notice set role as admin role
     * @param role role to set
     * @param adminRole admin role to set
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = _getRoleAdmin(role);
        AccessControlStorage.layout().roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /*
     * @notice assign role to given account
     * @param role role to assign
     * @param account recipient of role assignment
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        AccessControlStorage.layout().roles[role].members.add(account);
        emit RoleGranted(role, account, msg.sender);
    }

    /*
     * @notice unassign role from given account
     * @param role role to unassign
     * @parm account
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        AccessControlStorage.layout().roles[role].members.remove(account);
        emit RoleRevoked(role, account, msg.sender);
    }

    /**
     * @notice relinquish role
     * @param role role to relinquish
     */
    function _renounceRole(bytes32 role) internal virtual {
        _revokeRole(role, msg.sender);
    }
}

File 18 of 27 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { ReentrancyGuardStorage } from './ReentrancyGuardStorage.sol';

/**
 * @title Utility contract for preventing reentrancy attacks
 */
abstract contract ReentrancyGuard {
    error ReentrancyGuard__ReentrantCall();

    modifier nonReentrant() {
        ReentrancyGuardStorage.Layout storage l = ReentrancyGuardStorage
            .layout();
        if (l.status == 2) revert ReentrancyGuard__ReentrantCall();
        l.status = 2;
        _;
        l.status = 1;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

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

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract ModuleHelper {
    using EnumerableSet for EnumerableSet.AddressSet;

    bytes32 internal constant TRADERV0_POSITION = bytes32(uint256(keccak256("TraderV0.storage")) - 1);

    struct TraderV0Storage {
        string name;
        address feeReceiver;
        address vault;
        address baseAsset;
        uint256 performanceFeeRate;
        uint256 managementFeeRate;
        uint256 custodyTime;
        uint256 custodiedAmount;
        uint256 totalFees;
        EnumerableSet.AddressSet allowedTokens;
        EnumerableSet.AddressSet allowedSpenders;
        bool initialized;
    }

    function getTraderV0Storage() internal pure returns (TraderV0Storage storage storageStruct) {
        bytes32 position = TRADERV0_POSITION;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            storageStruct.slot := position
        }
    }

    function validateToken(address _token) internal view {
        require(getTraderV0Storage().allowedTokens.contains(_token), "Invalid token");
    }

    function validateSpender(address _token) internal view {
        require(getTraderV0Storage().allowedSpenders.contains(_token), "Invalid spender");
    }
}

File 21 of 27 : IDolomite.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.24;

interface IDolomiteStructs {

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

    enum ActionType {
        Deposit,   // supply tokens
        Withdraw,  // borrow tokens
        Transfer,  // transfer balance between accounts
        Buy,       // buy an amount of some token (externally)
        Sell,      // sell an amount of some token (externally)
        Trade,     // trade tokens against another account
        Liquidate, // liquidate an undercollateralized or expiring account
        Vaporize,  // use excess tokens to zero-out a completely negative account
        Call       // send arbitrary data to an address
    }

    enum AssetDenomination {
        Wei, // the amount is denominated in wei
        Par  // the amount is denominated in par
    }

    enum AssetReference {
        Delta, // the amount is given as a delta from the current value
        Target // the amount is given as an exact number to end up at
    }

    // ========================= Structs =========================

    struct AccountInfo {
        address owner;  // The address that owns the account
        uint256 number; // A nonce that allows a single address to control many accounts
    }

    /**
     * Most-recently-cached account status.
     *
     * Normal: Can only be liquidated if the account values are violating the global margin-ratio.
     * Liquid: Can be liquidated no matter the account values.
     *         Can be vaporized if there are no more positive account values.
     * Vapor:  Has only negative (or zeroed) account values. Can be vaporized.
     *
     */
    enum AccountStatus {
        Normal,
        Liquid,
        Vapor
    }

    /*
     * Arguments that are passed to DolomiteMargin in an ordered list as part of a single operation.
     * Each ActionArgs has an actionType which specifies which action struct that this data will be
     * parsed into before being processed.
     */
    struct ActionArgs {
        ActionType actionType;
        uint256 accountId;
        AssetAmount amount;
        uint256 primaryMarketId;
        uint256 secondaryMarketId;
        address otherAddress;
        uint256 otherAccountId;
        bytes data;
    }

    struct AssetAmount {
        bool sign; // true if positive
        AssetDenomination denomination;
        AssetReference ref;
        uint256 value;
    }

    struct Decimal {
        uint256 value;
    }

    struct InterestIndex {
        uint96 borrow;
        uint96 supply;
        uint32 lastUpdate;
    }

    struct Market {
        address token;

        // Whether additional borrows are allowed for this market
        bool isClosing;

        // Whether this market can be removed and its ID can be recycled and reused
        bool isRecyclable;

        // Total aggregated supply and borrow amount of the entire market
        TotalPar totalPar;

        // Interest index of the market
        InterestIndex index;

        // Contract address of the price oracle for this market
        IDolomitePriceOracle priceOracle;

        // Contract address of the interest setter for this market
        IDolomiteInterestSetter interestSetter;

        // Multiplier on the marginRatio for this market, IE 5% (0.05 * 1e18). This number increases the market's
        // required collateralization by: reducing the user's supplied value (in terms of dollars) for this market and
        // increasing its borrowed value. This is done through the following operation:
        // `suppliedWei = suppliedWei + (assetValueForThisMarket / (1 + marginPremium))`
        // This number increases the user's borrowed wei by multiplying it by:
        // `borrowedWei = borrowedWei + (assetValueForThisMarket * (1 + marginPremium))`
        Decimal marginPremium;

        // Multiplier on the liquidationSpread for this market, IE 20% (0.2 * 1e18). This number increases the
        // `liquidationSpread` using the following formula:
        // `liquidationSpread = liquidationSpread * (1 + spreadPremium)`
        // NOTE: This formula is applied up to two times - one for each market whose spreadPremium is greater than 0
        // (when performing a liquidation between two markets)
        Decimal spreadPremium;

        // The maximum amount that can be held by the external. This allows the external to cap any additional risk
        // that is inferred by allowing borrowing against low-cap or assets with increased volatility. Setting this
        // value to 0 is analogous to having no limit. This value can never be below 0.
        Wei maxWei;
    }

    struct MarketV2 {
        // Contract address of the associated ERC20 token
        address token;

        // Whether additional borrows are allowed for this market
        bool isClosing;

        // Total aggregated supply and borrow amount of the entire market
        TotalPar totalPar;

        // Interest index of the market
        InterestIndex index;

        // Contract address of the price oracle for this market
        IDolomitePriceOracle priceOracle;

        // Contract address of the interest setter for this market
        IDolomiteInterestSetter interestSetter;

        // Multiplier on the marginRatio for this market, IE 5% (0.05 * 1e18). This number increases the market's
        // required collateralization by: reducing the user's supplied value (in terms of dollars) for this market and
        // increasing its borrowed value. This is done through the following operation:
        // `suppliedWei = suppliedWei + (assetValueForThisMarket / (1 + marginPremium))`
        // This number increases the user's borrowed wei by multiplying it by:
        // `borrowedWei = borrowedWei + (assetValueForThisMarket * (1 + marginPremium))`
        Decimal marginPremium;

        // Multiplier on the liquidationSpread for this market, IE 20% (0.2 * 1e18). This number increases the
        // `liquidationSpread` using the following formula:
        // `liquidationSpread = liquidationSpread * (1 + spreadPremium)`
        // NOTE: This formula is applied up to two times - one for each market whose spreadPremium is greater than 0
        // (when performing a liquidation between two markets)
        Decimal liquidationSpreadPremium;

        // The maximum amount that can be held by the protocol. This allows the protocol to cap any additional risk
        // that is inferred by allowing borrowing against low-cap or assets with increased volatility. Setting this
        // value to 0 is analogous to having no limit. This value can never be below 0.
        Wei maxSupplyWei;

        // The maximum amount that can be borrowed by the protocol. This allows the protocol to cap any additional risk
        // that is inferred by allowing borrowing against low-cap or assets with increased volatility. Setting this
        // value to 0 is analogous to having no limit. This value can never be greater than 0.
        Wei maxBorrowWei;

        // The percentage of interest paid that is passed along from borrowers to suppliers. Setting this to 0 will
        // default to RiskParams.earningsRate.
        Decimal earningsRateOverride;
    }

    /*
     * The price of a base-unit of an asset. Has `36 - token.decimals` decimals
     */
    struct MonetaryPrice {
        uint256 value;
    }

    struct MonetaryValue {
        uint256 value;
    }

    struct OperatorArg {
        address operator;
        bool trusted;
    }

    struct Par {
        bool sign;
        uint128 value;
    }

    struct RiskLimits {
        // The highest that the ratio can be for liquidating under-water accounts
        uint64 marginRatioMax;
        // The highest that the liquidation rewards can be when a liquidator liquidates an account
        uint64 liquidationSpreadMax;
        // The highest that the supply APR can be for a market, as a proportion of the borrow rate. Meaning, a rate of
        // 100% (1e18) would give suppliers all of the interest that borrowers are paying. A rate of 90% would give
        // suppliers 90% of the interest that borrowers pay.
        uint64 earningsRateMax;
        // The highest min margin ratio premium that can be applied to a particular market. Meaning, a value of 100%
        // (1e18) would require borrowers to maintain an extra 100% collateral to maintain a healthy margin ratio. This
        // value works by increasing the debt owed and decreasing the supply held for the particular market by this
        // amount, plus 1e18 (since a value of 10% needs to be applied as `decimal.plusOne`)
        uint64 marginPremiumMax;
        // The highest liquidation reward that can be applied to a particular market. This percentage is applied
        // in addition to the liquidation spread in `RiskParams`. Meaning a value of 1e18 is 100%. It is calculated as:
        // `liquidationSpread * Decimal.onePlus(spreadPremium)`
        uint64 spreadPremiumMax;
        uint128 minBorrowedValueMax;
    }

    struct RiskLimitsV2 {
        // The highest that the ratio can be for liquidating under-water accounts
        uint64 marginRatioMax;
        // The highest that the liquidation rewards can be when a liquidator liquidates an account
        uint64 liquidationSpreadMax;
        // The highest that the supply APR can be for a market, as a proportion of the borrow rate. Meaning, a rate of
        // 100% (1e18) would give suppliers all of the interest that borrowers are paying. A rate of 90% would give
        // suppliers 90% of the interest that borrowers pay.
        uint64 earningsRateMax;
        // The highest min margin ratio premium that can be applied to a particular market. Meaning, a value of 100%
        // (1e18) would require borrowers to maintain an extra 100% collateral to maintain a healthy margin ratio. This
        // value works by increasing the debt owed and decreasing the supply held for the particular market by this
        // amount, plus 1e18 (since a value of 10% needs to be applied as `decimal.plusOne`)
        uint64 marginPremiumMax;
        // The highest liquidation reward that can be applied to a particular market. This percentage is applied
        // in addition to the liquidation spread in `RiskParams`. Meaning a value of 1e18 is 100%. It is calculated as:
        // `liquidationSpread * Decimal.onePlus(spreadPremium)`
        uint64 liquidationSpreadPremiumMax;
        // The highest that the borrow interest rate can ever be. If the rate returned is ever higher, the rate is
        // capped at this value instead of reverting. The goal is to keep Dolomite operational under all circumstances
        // instead of inadvertently DOS'ing the protocol.
        uint96 interestRateMax;
        // The highest that the minBorrowedValue can be. This is the minimum amount of value that must be borrowed.
        // Typically a value of $100 (100 * 1e18) is more than sufficient.
        uint128 minBorrowedValueMax;
    }

    struct RiskParams {
        // Required ratio of over-collateralization
        Decimal marginRatio;

        // Percentage penalty incurred by liquidated accounts
        Decimal liquidationSpread;

        // Percentage of the borrower's interest fee that gets passed to the suppliers
        Decimal earningsRate;

        // The minimum absolute borrow value of an account
        // There must be sufficient incentivize to liquidate undercollateralized accounts
        MonetaryValue minBorrowedValue;

        // The maximum number of markets a user can have a non-zero balance for a given account.
        uint256 accountMaxNumberOfMarketsWithBalances;
    }

    // The global risk parameters that govern the health and security of the system
    struct RiskParamsV2 {
        // Required ratio of over-collateralization
        Decimal marginRatio;

        // Percentage penalty incurred by liquidated accounts
        Decimal liquidationSpread;

        // Percentage of the borrower's interest fee that gets passed to the suppliers
        Decimal earningsRate;

        // The minimum absolute borrow value of an account
        // There must be sufficient incentivize to liquidate undercollateralized accounts
        MonetaryValue minBorrowedValue;

        // The maximum number of markets a user can have a non-zero balance for a given account.
        uint256 accountMaxNumberOfMarketsWithBalances;

        // The oracle sentinel used to disable borrowing/liquidations if the sequencer goes down
        IDolomiteOracleSentinel oracleSentinel;

        // The gas limit used for making callbacks via `IExternalCallback::onInternalBalanceChange` to smart contract
        // wallets. Setting to 0 will effectively disable callbacks; setting it super large is not desired since it
        // could lead to DOS attacks on the protocol; however, hard coding a max value isn't preferred since some chains
        // can calculate gas usage differently (like ArbGas before Arbitrum rolled out nitro)
        uint256 callbackGasLimit;

        // Certain addresses are allowed to borrow with different LTV requirements. When an account's risk is overrode,
        // the global risk parameters are ignored and the account's risk parameters are used instead.
        mapping(address => IDolomiteAccountRiskOverrideSetter) accountRiskOverrideSetterMap;
    }

    struct TotalPar {
        uint128 borrow;
        uint128 supply;
    }

    struct TotalWei {
        uint128 borrow;
        uint128 supply;
    }

    struct Wei {
        bool sign;
        uint256 value;
    }
}

interface IDolomiteMarginAdmin is IDolomiteStructs {

    // ============ Token Functions ============

    /**
     * Withdraw an ERC20 token for which there is an associated market. Only excess tokens can be withdrawn. The number
     * of excess tokens is calculated by taking the current number of tokens held in DolomiteMargin, adding the number
     * of tokens owed to DolomiteMargin by borrowers, and subtracting the number of tokens owed to suppliers by
     * DolomiteMargin.
     */
    function ownerWithdrawExcessTokens(
        uint256 marketId,
        address recipient
    )
    external
    returns (uint256);

    /**
     * Withdraw an ERC20 token for which there is no associated market.
     */
    function ownerWithdrawUnsupportedTokens(
        address token,
        address recipient
    )
    external
    returns (uint256);

    // ============ Market Functions ============

    /**
     * Sets the number of non-zero balances an account may have within the same `accountIndex`. This ensures a user
     * cannot DOS the system by filling their account with non-zero balances (which linearly increases gas costs when
     * checking collateralization) and disallowing themselves to close the position, because the number of gas units
     * needed to process their transaction exceed the block's gas limit. In turn, this would  prevent the user from also
     * being liquidated, causing the all of the capital to be "stuck" in the position.
     *
     * Lowering this number does not "freeze" user accounts that have more than the new limit of balances, because this
     * variable is enforced by checking the users number of non-zero balances against the max or if it sizes down before
     * each transaction finishes.
     */
    function ownerSetAccountMaxNumberOfMarketsWithBalances(
        uint256 accountMaxNumberOfMarketsWithBalances
    )
    external;

    /**
     * Add a new market to DolomiteMargin. Must be for a previously-unsupported ERC20 token.
     */
    function ownerAddMarket(
        address token,
        IDolomitePriceOracle priceOracle,
        IDolomiteInterestSetter interestSetter,
        Decimal calldata marginPremium,
        Decimal calldata spreadPremium,
        uint256 maxWei,
        bool isClosing,
        bool isRecyclable
    )
    external;

    /**
     * Removes a market from DolomiteMargin, sends any remaining tokens in this contract to `salvager` and invokes the
     * recyclable callback
     */
    function ownerRemoveMarkets(
        uint[] calldata marketIds,
        address salvager
    )
    external;

    /**
     * Set (or unset) the status of a market to "closing". The borrowedValue of a market cannot increase while its
     * status is "closing".
     */
    function ownerSetIsClosing(
        uint256 marketId,
        bool isClosing
    )
    external;

    /**
     * Set the price oracle for a market.
     */
    function ownerSetPriceOracle(
        uint256 marketId,
        IDolomitePriceOracle priceOracle
    )
    external;

    /**
     * Set the interest-setter for a market.
     */
    function ownerSetInterestSetter(
        uint256 marketId,
        IDolomiteInterestSetter interestSetter
    )
    external;

    /**
     * Set a premium on the minimum margin-ratio for a market. This makes it so that any positions that include this
     * market require a higher collateralization to avoid being liquidated.
     */
    function ownerSetMarginPremium(
        uint256 marketId,
        Decimal calldata marginPremium
    )
    external;

    function ownerSetMaxWei(
        uint256 marketId,
        uint256 maxWei
    )
    external;

    /**
     * Set a premium on the liquidation spread for a market. This makes it so that any liquidations that include this
     * market have a higher spread than the global default.
     */
    function ownerSetSpreadPremium(
        uint256 marketId,
        Decimal calldata spreadPremium
    )
    external;

    // ============ Risk Functions ============

    /**
     * Set the global minimum margin-ratio that every position must maintain to prevent being liquidated.
     */
    function ownerSetMarginRatio(
        Decimal calldata ratio
    )
    external;

    /**
     * Set the global liquidation spread. This is the spread between oracle prices that incentivizes the liquidation of
     * risky positions.
     */
    function ownerSetLiquidationSpread(
        Decimal calldata spread
    )
    external;

    /**
     * Set the global earnings-rate variable that determines what percentage of the interest paid by borrowers gets
     * passed-on to suppliers.
     */
    function ownerSetEarningsRate(
        Decimal calldata earningsRate
    )
    external;

    /**
     * Set the global minimum-borrow value which is the minimum value of any new borrow on DolomiteMargin.
     */
    function ownerSetMinBorrowedValue(
        MonetaryValue calldata minBorrowedValue
    )
    external;

    // ============ Global Operator Functions ============

    /**
     * Approve (or disapprove) an address that is permissioned to be an operator for all accounts in DolomiteMargin.
     * Intended only to approve smart-contracts.
     */
    function ownerSetGlobalOperator(
        address operator,
        bool approved
    )
    external;

    /**
     * Approve (or disapprove) an auto trader that can only be called by a global operator. IE for expirations
     */
    function ownerSetAutoTraderSpecial(
        address autoTrader,
        bool special
    )
    external;
}

interface IAuthorizationBase {
    function setIsCallerAuthorized(address _caller, bool _isAuthorized) external;

    function isCallerAuthorized(address _caller) external view returns (bool);
}

interface IDolomiteMargin is IDolomiteMarginAdmin {

    // ==================================================
    // ================= Write Functions ================
    // ==================================================

    /**
     * The main entry-point to DolomiteMargin that allows users and contracts to manage accounts.
     * Take one or more actions on one or more accounts. The msg.sender must be the owner or
     * operator of all accounts except for those being liquidated, vaporized, or traded with.
     * One call to operate() is considered a singular "operation". Account collateralization is
     * ensured only after the completion of the entire operation.
     *
     * @param  accounts  A list of all accounts that will be used in this operation. Cannot contain
     *                   duplicates. In each action, the relevant account will be referred-to by its
     *                   index in the list.
     * @param  actions   An ordered list of all actions that will be taken in this operation. The
     *                   actions will be processed in order.
     */
    function operate(
        AccountInfo[] calldata accounts,
        ActionArgs[] calldata actions
    ) external;

    /**
     * Approves/disapproves any number of operators. An operator is an external address that has the
     * same permissions to manipulate an account as the owner of the account. Operators are simply
     * addresses and therefore may either be externally-owned Ethereum accounts OR smart contracts.
     *
     * Operators are also able to act as AutoTrader contracts on behalf of the account owner if the
     * operator is a smart contract and implements the IAutoTrader interface.
     *
     * @param  args  A list of OperatorArgs which have an address and a boolean. The boolean value
     *               denotes whether to approve (true) or revoke approval (false) for that address.
     */
    function setOperators(
        OperatorArg[] calldata args
    ) external;

    // ==================================================
    // ================= Read Functions ================
    // ==================================================

    // ============ Getters for Markets ============

    /**
     * Get the ERC20 token address for a market.
     *
     * @param  token    The token to query
     * @return          The token's marketId if the token is valid
     */
    function getMarketIdByTokenAddress(
        address token
    ) external view returns (uint256);

    /**
     * Get the ERC20 token address for a market.
     *
     * @param  marketId  The market to query
     * @return           The token address
     */
    function getMarketTokenAddress(
        uint256 marketId
    ) external view returns (address);

    /**
     * Return the maximum amount of the market that can be supplied on Dolomite. Always 0 or positive.
     *
     * @param  marketId  The market to query
     * @return           The max amount of the market that can be supplied
     */
    function getMarketMaxWei(
        uint256 marketId
    ) external view returns (Wei memory);

    /**
     * Return true if a particular market is in closing mode. Additional borrows cannot be taken
     * from a market that is closing.
     *
     * @param  marketId  The market to query
     * @return           True if the market is closing
     */
    function getMarketIsClosing(
        uint256 marketId
    )
    external
    view
    returns (bool);

    /**
     * Get the price of the token for a market.
     *
     * @param  marketId  The market to query
     * @return           The price of each atomic unit of the token
     */
    function getMarketPrice(
        uint256 marketId
    ) external view returns (MonetaryPrice memory);

    /**
     * Get the total number of markets.
     *
     * @return  The number of markets
     */
    function getNumMarkets() external view returns (uint256);

    /**
     * Get the total principal amounts (borrowed and supplied) for a market.
     *
     * @param  marketId  The market to query
     * @return           The total principal amounts
     */
    function getMarketTotalPar(
        uint256 marketId
    ) external view returns (TotalPar memory);

    /**
     * Get the most recently cached interest index for a market.
     *
     * @param  marketId  The market to query
     * @return           The most recent index
     */
    function getMarketCachedIndex(
        uint256 marketId
    ) external view returns (InterestIndex memory);

    /**
     * Get the interest index for a market if it were to be updated right now.
     *
     * @param  marketId  The market to query
     * @return           The estimated current index
     */
    function getMarketCurrentIndex(
        uint256 marketId
    ) external view returns (InterestIndex memory);

    /**
     * Get the price oracle address for a market.
     *
     * @param  marketId  The market to query
     * @return           The price oracle address
     */
    function getMarketPriceOracle(
        uint256 marketId
    ) external view returns (IDolomitePriceOracle);

    /**
     * Get the interest-setter address for a market.
     *
     * @param  marketId  The market to query
     * @return           The interest-setter address
     */
    function getMarketInterestSetter(
        uint256 marketId
    ) external view returns (IDolomiteInterestSetter);

    /**
     * Get the margin premium for a market. A margin premium makes it so that any positions that
     * include the market require a higher collateralization to avoid being liquidated.
     *
     * @param  marketId  The market to query
     * @return           The market's margin premium
     */
    function getMarketMarginPremium(
        uint256 marketId
    ) external view returns (Decimal memory);

    /**
     * Get the spread premium for a market. A spread premium makes it so that any liquidations
     * that include the market have a higher spread than the global default.
     *
     * @param  marketId  The market to query
     * @return           The market's spread premium
     */
    function getMarketSpreadPremium(
        uint256 marketId
    ) external view returns (Decimal memory);

    /**
     * Return true if this market can be removed and its ID can be recycled and reused
     *
     * @param  marketId  The market to query
     * @return           True if the market is recyclable
     */
    function getMarketIsRecyclable(
        uint256 marketId
    ) external view returns (bool);

    /**
     * Gets the recyclable markets, up to `n` length. If `n` is greater than the length of the list, 0's are returned
     * for the empty slots.
     *
     * @param  n    The number of markets to get, bounded by the linked list being smaller than `n`
     * @return      The list of recyclable markets, in the same order held by the linked list
     */
    function getRecyclableMarkets(
        uint256 n
    ) external view returns (uint[] memory);

    /**
     * Get the current borrower interest rate for a market.
     *
     * @param  marketId  The market to query
     * @return           The current interest rate
     */
    function getMarketInterestRate(
        uint256 marketId
    ) external view returns (IDolomiteInterestSetter.InterestRate memory);

    /**
     * Get basic information about a particular market.
     *
     * @param  marketId  The market to query
     * @return           A Market struct with the current state of the market
     */
    function getMarket(
        uint256 marketId
    ) external view returns (Market memory);

    /**
     * Get comprehensive information about a particular market.
     *
     * @param  marketId  The market to query
     * @return           A tuple containing the values:
     *                    - A Market struct with the current state of the market
     *                    - The current estimated interest index
     *                    - The current token price
     *                    - The current market interest rate
     */
    function getMarketWithInfo(
        uint256 marketId
    )
    external
    view
    returns (
        Market memory,
        InterestIndex memory,
        MonetaryPrice memory,
        IDolomiteInterestSetter.InterestRate memory
    );

    /**
     * Get the number of excess tokens for a market. The number of excess tokens is calculated by taking the current
     * number of tokens held in DolomiteMargin, adding the number of tokens owed to DolomiteMargin by borrowers, and
     * subtracting the number of tokens owed to suppliers by DolomiteMargin.
     *
     * @param  marketId  The market to query
     * @return           The number of excess tokens
     */
    function getNumExcessTokens(
        uint256 marketId
    ) external view returns (Wei memory);

    // ============ Getters for Accounts ============

    /**
     * Get the principal value for a particular account and market.
     *
     * @param  account   The account to query
     * @param  marketId  The market to query
     * @return           The principal value
     */
    function getAccountPar(
        AccountInfo calldata account,
        uint256 marketId
    ) external view returns (Par memory);

    /**
     * Get the principal value for a particular account and market, with no check the market is valid. Meaning, markets
     * that don't exist return 0.
     *
     * @param  account   The account to query
     * @param  marketId  The market to query
     * @return           The principal value
     */
    function getAccountParNoMarketCheck(
        AccountInfo calldata account,
        uint256 marketId
    ) external view returns (Par memory);

    /**
     * Get the token balance for a particular account and market.
     *
     * @param  account   The account to query
     * @param  marketId  The market to query
     * @return           The token amount
     */
    function getAccountWei(
        AccountInfo calldata account,
        uint256 marketId
    ) external view returns (Wei memory);

    /**
     * Get the status of an account (Normal, Liquidating, or Vaporizing).
     *
     * @param  account  The account to query
     * @return          The account's status
     */
    function getAccountStatus(
        AccountInfo calldata account
    ) external view returns (AccountStatus);

    /**
     * Get a list of markets that have a non-zero balance for an account
     *
     * @param  account  The account to query
     * @return          The non-sorted marketIds with non-zero balance for the account.
     */
    function getAccountMarketsWithBalances(
        AccountInfo calldata account
    ) external view returns (uint256[] memory);

    /**
     * Get the number of markets that have a non-zero balance for an account
     *
     * @param  account  The account to query
     * @return          The non-sorted marketIds with non-zero balance for the account.
     */
    function getAccountNumberOfMarketsWithBalances(
        AccountInfo calldata account
    ) external view returns (uint256);

    /**
     * Get the marketId for an account's market with a non-zero balance at the given index
     *
     * @param  account  The account to query
     * @return          The non-sorted marketIds with non-zero balance for the account.
     */
    function getAccountMarketWithBalanceAtIndex(
        AccountInfo calldata account,
        uint256 index
    ) external view returns (uint256);

    /**
     * Get the number of markets with which an account has a negative balance.
     *
     * @param  account  The account to query
     * @return          The non-sorted marketIds with non-zero balance for the account.
     */
    function getAccountNumberOfMarketsWithDebt(
        AccountInfo calldata account
    ) external view returns (uint256);

    /**
     * Get the total supplied and total borrowed value of an account.
     *
     * @param  account  The account to query
     * @return          The following values:
     *                   - The supplied value of the account
     *                   - The borrowed value of the account
     */
    function getAccountValues(
        AccountInfo calldata account
    ) external view returns (MonetaryValue memory, MonetaryValue memory);

    /**
     * Get the total supplied and total borrowed values of an account adjusted by the marginPremium
     * of each market. Supplied values are divided by (1 + marginPremium) for each market and
     * borrowed values are multiplied by (1 + marginPremium) for each market. Comparing these
     * adjusted values gives the margin-ratio of the account which will be compared to the global
     * margin-ratio when determining if the account can be liquidated.
     *
     * @param  account  The account to query
     * @return          The following values:
     *                   - The supplied value of the account (adjusted for marginPremium)
     *                   - The borrowed value of the account (adjusted for marginPremium)
     */
    function getAdjustedAccountValues(
        AccountInfo calldata account
    ) external view returns (MonetaryValue memory, MonetaryValue memory);

    /**
     * Get an account's summary for each market.
     *
     * @param  account  The account to query
     * @return          The following values:
     *                   - The market IDs for each market
     *                   - The ERC20 token address for each market
     *                   - The account's principal value for each market
     *                   - The account's (supplied or borrowed) number of tokens for each market
     */
    function getAccountBalances(
        AccountInfo calldata account
    ) external view returns (uint[] memory, address[] memory, Par[] memory, Wei[] memory);

    // ============ Getters for Account Permissions ============

    /**
     * Return true if a particular address is approved as an operator for an owner's accounts.
     * Approved operators can act on the accounts of the owner as if it were the operator's own.
     *
     * @param  owner     The owner of the accounts
     * @param  operator  The possible operator
     * @return           True if operator is approved for owner's accounts
     */
    function getIsLocalOperator(
        address owner,
        address operator
    ) external view returns (bool);

    /**
     * Return true if a particular address is approved as a global operator. Such an address can
     * act on any account as if it were the operator's own.
     *
     * @param  operator  The address to query
     * @return           True if operator is a global operator
     */
    function getIsGlobalOperator(
        address operator
    ) external view returns (bool);

    /**
     * Checks if the autoTrader can only be called invoked by a global operator
     *
     * @param  autoTrader    The trader that should be checked for special call privileges.
     */
    function getIsAutoTraderSpecial(address autoTrader) external view returns (bool);

    /**
     * @return The address that owns the DolomiteMargin protocol
     */
    function owner() external view returns (address);

    // ============ Getters for Risk Params ============

    /**
     * Get the global minimum margin-ratio that every position must maintain to prevent being
     * liquidated.
     *
     * @return  The global margin-ratio
     */
    function getMarginRatio() external view returns (Decimal memory);

    /**
     * Get the global liquidation spread. This is the spread between oracle prices that incentivizes
     * the liquidation of risky positions.
     *
     * @return  The global liquidation spread
     */
    function getLiquidationSpread() external view returns (Decimal memory);

    /**
     * Get the adjusted liquidation spread for some market pair. This is equal to the global
     * liquidation spread multiplied by (1 + spreadPremium) for each of the two markets.
     *
     * @param  heldMarketId  The market for which the account has collateral
     * @param  owedMarketId  The market for which the account has borrowed tokens
     * @return               The adjusted liquidation spread
     */
    function getLiquidationSpreadForPair(
        uint256 heldMarketId,
        uint256 owedMarketId
    ) external view returns (Decimal memory);

    /**
     * Get the global earnings-rate variable that determines what percentage of the interest paid
     * by borrowers gets passed-on to suppliers.
     *
     * @return  The global earnings rate
     */
    function getEarningsRate() external view returns (Decimal memory);

    /**
     * Get the global minimum-borrow value which is the minimum value of any new borrow on DolomiteMargin.
     *
     * @return  The global minimum borrow value
     */
    function getMinBorrowedValue() external view returns (MonetaryValue memory);

    /**
     * Get all risk parameters in a single struct.
     *
     * @return  All global risk parameters
     */
    function getRiskParams() external view returns (RiskParams memory);

    /**
     * Get all risk parameter limits in a single struct. These are the maximum limits at which the
     * risk parameters can be set by the admin of DolomiteMargin.
     *
     * @return  All global risk parameter limits
     */
    function getRiskLimits() external view returns (RiskLimits memory);
}

interface IDolomiteAccountRiskOverrideSetter {

    /**
     * @notice  Gets the risk overrides for a given account owner.
     *
     * @param  _accountOwner               The owner of the account whose risk override should be retrieved.
     * @return  marginRatioOverride         The margin ratio override for this account.
     * @return  liquidationSpreadOverride   The liquidation spread override for this account.
     */
    function getAccountRiskOverride(
        address _accountOwner
    )
    external
    view
    returns
    (
        IDolomiteStructs.Decimal memory marginRatioOverride,
        IDolomiteStructs.Decimal memory liquidationSpreadOverride
    );
}

interface IDolomiteInterestSetter {

    // ============ Enum ============

    enum InterestSetterType {
        None,
        Linear,
        DoubleExponential,
        Other
    }

    // ============ Structs ============

    struct InterestRate {
        uint256 value;
    }

    // ============ Functions ============

    /**
     * Get the interest rate of a token given some borrowed and supplied amounts
     *
     * @param  token        The address of the ERC20 token for the market
     * @param  borrowWei    The total borrowed token amount for the market
     * @param  supplyWei    The total supplied token amount for the market
     * @return              The interest rate per second
     */
    function getInterestRate(
        address token,
        uint256 borrowWei,
        uint256 supplyWei
    )
    external
    view
    returns (InterestRate memory);

    function interestSetterType() external pure returns (InterestSetterType);
}

interface IDolomiteOracleSentinel {

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

    event GracePeriodSet(
        uint256 gracePeriod
    );

    // ============ Functions ============

    /**
     * @dev Allows the owner to set the grace period duration, which specifies how long the system will disallow
     *      liquidations after sequencer is back online. Only callable by the owner.
     *
     * @param  _gracePeriod  The new duration of the grace period
     */
    function ownerSetGracePeriod(
        uint256 _gracePeriod
    )
    external;

    /**
     * @return True if new borrows should be allowed, false otherwise
     */
    function isBorrowAllowed() external view returns (bool);

    /**
     * @return True if liquidations should be allowed, false otherwise
     */
    function isLiquidationAllowed() external view returns (bool);

    /**
     * @return  The duration between when the feed comes back online and when the system will allow liquidations to be
     *          processed normally
     */
    function gracePeriod() external view returns (uint256);
}


interface IDolomitePriceOracle {

    // ============ Public Functions ============

    /**
     * Get the price of a token
     *
     * @param  token  The ERC20 token address of the market
     * @return        The USD price of a base unit of the token, then multiplied by 10^(36 - decimals).
     *                So a USD-stable coin with 6 decimal places would return `price * 10^30`.
     *                This is the price of the base unit rather than the price of a "human-readable"
     *                token amount. Every ERC20 may have a different number of decimals.
     */
    function getPrice(
        address token
    )
    external
    view
    returns (IDolomiteStructs.MonetaryPrice memory);
}


library Require {

    // ============ Constants ============

    uint256 private constant _ASCII_ZERO = 48; // '0'
    uint256 private constant _ASCII_RELATIVE_ZERO = 87; // 'a' - 10
    uint256 private constant _ASCII_LOWER_EX = 120; // 'x'
    bytes2 private constant _COLON = 0x3a20; // ': '
    bytes2 private constant _COMMA = 0x2c20; // ', '
    bytes2 private constant _LPAREN = 0x203c; // ' <'
    bytes1 private constant _RPAREN = 0x3e; // '>'
    uint256 private constant _FOUR_BIT_MASK = 0xf;

    // ============ Library Functions ============

    function that(
        bool must,
        bytes32 file,
        bytes32 reason
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason)
                )
            )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        uint256 payloadA
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason),
                    _LPAREN,
                    _stringify(payloadA),
                    _RPAREN
                )
            )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        uint256 payloadA,
        uint256 payloadB
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason),
                    _LPAREN,
                    _stringify(payloadA),
                    _COMMA,
                    _stringify(payloadB),
                    _RPAREN
                )
            )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        address payloadA
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason),
                    _LPAREN,
                    _stringify(payloadA),
                    _RPAREN
                )
            )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        address payloadA,
        uint256 payloadB
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason),
                    _LPAREN,
                    _stringify(payloadA),
                    _COMMA,
                    _stringify(payloadB),
                    _RPAREN
                )
            )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        address payloadA,
        uint256 payloadB,
        uint256 payloadC
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason),
                    _LPAREN,
                    _stringify(payloadA),
                    _COMMA,
                    _stringify(payloadB),
                    _COMMA,
                    _stringify(payloadC),
                    _RPAREN
                )
            )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        bytes32 payloadA
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason),
                    _LPAREN,
                    _stringify(payloadA),
                    _RPAREN
                )
            )
            );
        }
    }

    function that(
        bool must,
        bytes32 file,
        bytes32 reason,
        bytes32 payloadA,
        uint256 payloadB,
        uint256 payloadC
    )
    internal
    pure
    {
        if (!must) {
            revert(
            string(
                abi.encodePacked(
                    stringifyTruncated(file),
                    _COLON,
                    stringifyTruncated(reason),
                    _LPAREN,
                    _stringify(payloadA),
                    _COMMA,
                    _stringify(payloadB),
                    _COMMA,
                    _stringify(payloadC),
                    _RPAREN
                )
            )
            );
        }
    }

    // ============ Private Functions ============

    function stringifyTruncated(
        bytes32 input
    )
    internal
    pure
    returns (bytes memory)
    {
        // put the input bytes into the result
        bytes memory result = abi.encodePacked(input);

        // determine the length of the input by finding the location of the last non-zero byte
        for (uint256 i = 32; i > 0; ) {
            // reverse-for-loops with unsigned integer
            i--;

            // find the last non-zero byte in order to determine the length
            if (result[i] != 0) {
                uint256 length = i + 1;

                /* solhint-disable-next-line no-inline-assembly */
                assembly {
                    mstore(result, length) // r.length = length;
                }

                return result;
            }
        }

        // all bytes are zero
        return new bytes(0);
    }

    function stringifyFunctionSelector(
        bytes4 input
    )
    internal
    pure
    returns (bytes memory)
    {
        uint256 z = uint256(bytes32(input) >> 224);

        // bytes4 are "0x" followed by 4 bytes of data which take up 2 characters each
        bytes memory result = new bytes(10);

        // populate the result with "0x"
        result[0] = bytes1(uint8(_ASCII_ZERO));
        result[1] = bytes1(uint8(_ASCII_LOWER_EX));

        // for each byte (starting from the lowest byte), populate the result with two characters
        for (uint256 i = 0; i < 4; i++) {
            // each byte takes two characters
            uint256 shift = i * 2;

            // populate the least-significant character
            result[9 - shift] = _char(z & _FOUR_BIT_MASK);
            z = z >> 4;

            // populate the most-significant character
            result[8 - shift] = _char(z & _FOUR_BIT_MASK);
            z = z >> 4;
        }

        return result;
    }

    function _stringify(
        uint256 input
    )
    private
    pure
    returns (bytes memory)
    {
        if (input == 0) {
            return "0";
        }

        // get the final string length
        uint256 j = input;
        uint256 length;
        while (j != 0) {
            length++;
            j /= 10;
        }

        // allocate the string
        bytes memory bstr = new bytes(length);

        // populate the string starting with the least-significant character
        j = input;
        for (uint256 i = length; i > 0; ) {
            // reverse-for-loops with unsigned integer
            i--;

            // take last decimal digit
            bstr[i] = bytes1(uint8(_ASCII_ZERO + (j % 10)));

            // remove the last decimal digit
            j /= 10;
        }

        return bstr;
    }

    function _stringify(
        address input
    )
    private
    pure
    returns (bytes memory)
    {
        uint256 z = uint256(uint160(input));

        // addresses are "0x" followed by 20 bytes of data which take up 2 characters each
        bytes memory result = new bytes(42);

        // populate the result with "0x"
        result[0] = bytes1(uint8(_ASCII_ZERO));
        result[1] = bytes1(uint8(_ASCII_LOWER_EX));

        // for each byte (starting from the lowest byte), populate the result with two characters
        for (uint256 i = 0; i < 20; i++) {
            // each byte takes two characters
            uint256 shift = i * 2;

            // populate the least-significant character
            result[41 - shift] = _char(z & _FOUR_BIT_MASK);
            z = z >> 4;

            // populate the most-significant character
            result[40 - shift] = _char(z & _FOUR_BIT_MASK);
            z = z >> 4;
        }

        return result;
    }

    function _stringify(
        bytes32 input
    )
    private
    pure
    returns (bytes memory)
    {
        uint256 z = uint256(input);

        // bytes32 are "0x" followed by 32 bytes of data which take up 2 characters each
        bytes memory result = new bytes(66);

        // populate the result with "0x"
        result[0] = bytes1(uint8(_ASCII_ZERO));
        result[1] = bytes1(uint8(_ASCII_LOWER_EX));

        // for each byte (starting from the lowest byte), populate the result with two characters
        for (uint256 i = 0; i < 32; i++) {
            // each byte takes two characters
            uint256 shift = i * 2;

            // populate the least-significant character
            result[65 - shift] = _char(z & _FOUR_BIT_MASK);
            z = z >> 4;

            // populate the most-significant character
            result[64 - shift] = _char(z & _FOUR_BIT_MASK);
            z = z >> 4;
        }

        return result;
    }

    function _char(
        uint256 input
    )
    private
    pure
    returns (bytes1)
    {
        // return ASCII digit (0-9)
        if (input < 10) {
            return bytes1(uint8(input + _ASCII_ZERO));
        }

        // return ASCII letter (a-f)
        return bytes1(uint8(input + _ASCII_RELATIVE_ZERO));
    }
}


library DolomiteMarginMath {

    // ============ Constants ============

    bytes32 internal constant _FILE = "DolomiteMarginMath";

    // ============ Library Functions ============

    /*
     * Return target * (numerator / denominator).
     */
    function getPartial(
        uint256 target,
        uint256 numerator,
        uint256 denominator
    )
    internal
    pure
    returns (uint256)
    {
        return target * numerator / denominator;
    }

    /*
     * Return target * (numerator / denominator), but rounded half-up. Meaning, a result of 101.1 rounds to 102
     * instead of 101.
     */
    function getPartialRoundUp(
        uint256 target,
        uint256 numerator,
        uint256 denominator
    )
    internal
    pure
    returns (uint256)
    {
        if (target == 0 || numerator == 0) {
            return 0;
        }
        return (((target * numerator) - 1) / denominator) + 1;
    }

    /*
     * Return target * (numerator / denominator), but rounded half-up. Meaning, a result of 101.5 rounds to 102
     * instead of 101.
     */
    function getPartialRoundHalfUp(
        uint256 target,
        uint256 numerator,
        uint256 denominator
    )
    internal
    pure
    returns (uint256)
    {
        if (target == 0 || numerator == 0) {
            return 0;
        }
        return (((target * numerator) + (denominator / 2)) / denominator);
    }

    function to128(
        uint256 number
    )
    internal
    pure
    returns (uint128)
    {
        uint128 result = uint128(number);
        Require.that(
            result == number,
            _FILE,
            "Unsafe cast to uint128",
            number
        );
        return result;
    }

    function to96(
        uint256 number
    )
    internal
    pure
    returns (uint96)
    {
        uint96 result = uint96(number);
        Require.that(
            result == number,
            _FILE,
            "Unsafe cast to uint96",
            number
        );
        return result;
    }

    function to32(
        uint256 number
    )
    internal
    pure
    returns (uint32)
    {
        uint32 result = uint32(number);
        Require.that(
            result == number,
            _FILE,
            "Unsafe cast to uint32",
            number
        );
        return result;
    }
}

library TypesLib {
    using DolomiteMarginMath for uint256;

    // ============ Par (Principal Amount) ============

    function zeroPar()
    internal
    pure
    returns (IDolomiteStructs.Par memory)
    {
        return IDolomiteStructs.Par({
            sign: false,
            value: 0
        });
    }

    function sub(
        IDolomiteStructs.Par memory a,
        IDolomiteStructs.Par memory b
    )
    internal
    pure
    returns (IDolomiteStructs.Par memory)
    {
        return add(a, negative(b));
    }

    function add(
        IDolomiteStructs.Par memory a,
        IDolomiteStructs.Par memory b
    )
    internal
    pure
    returns (IDolomiteStructs.Par memory)
    {
        IDolomiteStructs.Par memory result;
        if (a.sign == b.sign) {
            result.sign = a.sign;
            result.value = a.value + b.value;
        } else {
            if (a.value >= b.value) {
                result.sign = a.sign;
                result.value = a.value - b.value;
            } else {
                result.sign = b.sign;
                result.value = b.value - a.value;
            }
        }
        return result;
    }

    function equals(
        IDolomiteStructs.Par memory a,
        IDolomiteStructs.Par memory b
    )
    internal
    pure
    returns (bool)
    {
        if (a.value == b.value) {
            if (a.value == 0) {
                return true;
            }
            return a.sign == b.sign;
        }
        return false;
    }

    function negative(
        IDolomiteStructs.Par memory a
    )
    internal
    pure
    returns (IDolomiteStructs.Par memory)
    {
        return IDolomiteStructs.Par({
            sign: !a.sign,
            value: a.value
        });
    }

    function isNegative(
        IDolomiteStructs.Par memory a
    )
    internal
    pure
    returns (bool)
    {
        return !a.sign && a.value > 0;
    }

    function isPositive(
        IDolomiteStructs.Par memory a
    )
    internal
    pure
    returns (bool)
    {
        return a.sign && a.value > 0;
    }

    function isZero(
        IDolomiteStructs.Par memory a
    )
    internal
    pure
    returns (bool)
    {
        return a.value == 0;
    }

    function isLessThanZero(
        IDolomiteStructs.Par memory a
    )
    internal
    pure
    returns (bool)
    {
        return a.value > 0 && !a.sign;
    }

    function isGreaterThanOrEqualToZero(
        IDolomiteStructs.Par memory a
    )
    internal
    pure
    returns (bool)
    {
        return isZero(a) || a.sign;
    }

    // ============ Wei (Token Amount) ============

    function zeroWei()
    internal
    pure
    returns (IDolomiteStructs.Wei memory)
    {
        return IDolomiteStructs.Wei({
            sign: false,
            value: 0
        });
    }

    function sub(
        IDolomiteStructs.Wei memory a,
        IDolomiteStructs.Wei memory b
    )
    internal
    pure
    returns (IDolomiteStructs.Wei memory)
    {
        return add(a, negative(b));
    }

    function add(
        IDolomiteStructs.Wei memory a,
        IDolomiteStructs.Wei memory b
    )
    internal
    pure
    returns (IDolomiteStructs.Wei memory)
    {
        IDolomiteStructs.Wei memory result;
        if (a.sign == b.sign) {
            result.sign = a.sign;
            result.value = a.value + b.value;
        } else {
            if (a.value >= b.value) {
                result.sign = a.sign;
                result.value = a.value - b.value;
            } else {
                result.sign = b.sign;
                result.value = b.value - a.value;
            }
        }
        return result;
    }

    function equals(
        IDolomiteStructs.Wei memory a,
        IDolomiteStructs.Wei memory b
    )
    internal
    pure
    returns (bool)
    {
        if (a.value == b.value) {
            if (a.value == 0) {
                return true;
            }
            return a.sign == b.sign;
        }
        return false;
    }

    function negative(
        IDolomiteStructs.Wei memory a
    )
    internal
    pure
    returns (IDolomiteStructs.Wei memory)
    {
        return IDolomiteStructs.Wei({
            sign: !a.sign,
            value: a.value
        });
    }

    function isNegative(
        IDolomiteStructs.Wei memory a
    )
    internal
    pure
    returns (bool)
    {
        return !a.sign && a.value > 0;
    }

    function isPositive(
        IDolomiteStructs.Wei memory a
    )
    internal
    pure
    returns (bool)
    {
        return a.sign && a.value > 0;
    }

    function isZero(
        IDolomiteStructs.Wei memory a
    )
    internal
    pure
    returns (bool)
    {
        return a.value == 0;
    }
}

library AccountBalanceLib {
    using TypesLib for IDolomiteStructs.Par;

    // ============ Types ============

    /// Checks that either BOTH, FROM, or TO accounts all have non-negative balances
    enum BalanceCheckFlag {
        Both,
        From,
        To,
        None
    }

    // ============ Constants ============

    bytes32 private constant _FILE = "AccountBalanceLib";

    // ============ Functions ============

    /**
     *  Checks that the account's balance is non-negative. Reverts if the check fails
     */
    function verifyBalanceIsNonNegative(
        IDolomiteMargin dolomiteMargin,
        address _accountOwner,
        uint256 _accountNumber,
        uint256 _marketId
    ) internal view {
        IDolomiteStructs.AccountInfo memory account = IDolomiteStructs.AccountInfo({
            owner: _accountOwner,
            number: _accountNumber
        });
        IDolomiteStructs.Par memory par = dolomiteMargin.getAccountPar(account, _marketId);
        Require.that(
            par.isPositive() || par.isZero(),
            _FILE,
            "account cannot go negative",
            _accountOwner,
            _accountNumber,
            _marketId
        );
    }
}

interface IDepositWithdrawalProxy {
    // ========================= Functions =========================

    function initializeETHMarket(address payable _weth) external;

    /**
     *
     * @param  _toAccountIndex  The index into which `msg.sender` will be depositing
     * @param  _marketId        The ID of the market being deposited
     * @param  _amountWei       The amount, in Wei, to deposit. Use `uint(-1)` to deposit `msg.sender`'s entire balance
     */
    function depositWei(
        uint256 _toAccountIndex,
        uint256 _marketId,
        uint256 _amountWei
    ) external;

    /**
     * Same as `depositWei` but converts the `msg.sender`'s sent ETH into WETH before depositing into `DolomiteMargin`.
     *
     * @param  _toAccountIndex  The index into which `msg.sender` will be depositing
     */
    function depositETH(
        uint256 _toAccountIndex
    ) external payable;

    /**
     * @dev Same as `depositWei` but defaults to account index 0 to save additional call data
     *
     * @param  _marketId    The ID of the market being deposited
     * @param  _amountWei   The amount, in Wei, to deposit. Use `uint(-1)` to deposit `msg.sender`'s entire balance
     */
    function depositWeiIntoDefaultAccount(
        uint256 _marketId,
        uint256 _amountWei
    ) external;

    /**
     * Same as `depositWeiIntoDefaultAccount` but converts the `msg.sender`'s sent ETH into WETH before depositing into
     * `DolomiteMargin`.
     */
    function depositETHIntoDefaultAccount() external payable;

    /**
     *
     * @param  _fromAccountIndex    The index from which `msg.sender` will be withdrawing
     * @param  _marketId            The ID of the market being withdrawn
     * @param  _amountWei           The amount, in Wei, to withdraw. Use `uint(-1)` to withdraw `msg.sender`'s entire
     *                              balance
     * @param  _balanceCheckFlag    Use `BalanceCheckFlag.Both` or `BalanceCheckFlag.From` to check that
     *                              `_fromAccountIndex` balance is non-negative after the withdrawal settles.
     */
    function withdrawWei(
        uint256 _fromAccountIndex,
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     * Same as `withdrawWei` but for withdrawing ETH. The user will receive unwrapped ETH from DolomiteMargin.
     *
     * @param  _fromAccountIndex    The index from which `msg.sender` will be withdrawing
     * @param  _amountWei           The amount, in Wei, to withdraw. Use `uint(-1)` to withdraw `msg.sender`'s entire
     *                              balance.
     * @param  _balanceCheckFlag    Use `BalanceCheckFlag.Both` or `BalanceCheckFlag.From` to check that
     *                              `_fromAccountIndex` balance is non-negative after the withdrawal settles.
     */
    function withdrawETH(
        uint256 _fromAccountIndex,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     * @dev Same as `withdrawWei` but defaults to account index 0 to save additional call data
     *
     * @param  _marketId            The ID of the market being withdrawn
     * @param  _amountWei           The amount, in Wei, to withdraw. Use `uint(-1)` to withdraw `msg.sender`'s entire
     *                              balance
     * @param  _balanceCheckFlag    Use `BalanceCheckFlag.Both` or `BalanceCheckFlag.From` to check that
     *                              `_fromAccountIndex` balance is non-negative after the withdrawal settles.
     */
    function withdrawWeiFromDefaultAccount(
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     * Same as `withdrawWeiFromDefaultAccount` but for withdrawing ETH. The user will receive unwrapped ETH from
     * DolomiteMargin.
     *
     * @param  _amountWei           The amount, in Wei, to withdraw. Use `uint(-1)` to withdraw `msg.sender`'s entire
     *                              balance
     * @param  _balanceCheckFlag    Use `BalanceCheckFlag.Both` or `BalanceCheckFlag.From` to check that
     *                              `_fromAccountIndex` balance is non-negative after the withdrawal settles.
     */
    function withdrawETHFromDefaultAccount(
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     *
     * @param  _toAccountIndex  The index into which `msg.sender` will be depositing
     * @param  _marketId        The ID of the market being deposited
     * @param  _amountPar       The amount, in Par, to deposit.
     */
    function depositPar(
        uint256 _toAccountIndex,
        uint256 _marketId,
        uint256 _amountPar
    ) external;

    /**
     * @dev Same as `depositPar` but defaults to account index 0 to save additional call data
     *
     * @param  _marketId    The ID of the market being deposited
     * @param  _amountPar   The amount, in Par, to deposit.
     */
    function depositParIntoDefaultAccount(
        uint256 _marketId,
        uint256 _amountPar
    ) external;

    /**
     *
     * @param  _fromAccountIndex    The index from which `msg.sender` will be withdrawing
     * @param  _marketId            The ID of the market being withdrawn
     * @param  _amountPar           The amount, in Par, to withdraw. Use `uint(-1)` to withdraw `msg.sender`'s entire
     *                              balance
     * @param  _balanceCheckFlag    Use `BalanceCheckFlag.Both` or `BalanceCheckFlag.From` to check that
     *                              `_fromAccountIndex` balance is non-negative after the withdrawal settles.
     */
    function withdrawPar(
        uint256 _fromAccountIndex,
        uint256 _marketId,
        uint256 _amountPar,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     * @dev Same as `withdrawPar` but defaults to account index 0 to save additional call data
     *
     * @param  _marketId            The ID of the market being withdrawn
     * @param  _amountPar           The amount, in Par, to withdraw. Use `uint(-1)` to withdraw `msg.sender`'s entire
     *                              balance
     * @param  _balanceCheckFlag    Use `BalanceCheckFlag.Both` or `BalanceCheckFlag.From` to check that
     *                              `_fromAccountIndex` balance is non-negative after the withdrawal settles.
     */
    function withdrawParFromDefaultAccount(
        uint256 _marketId,
        uint256 _amountPar,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;
}

interface IBorrowPositionProxyV1 {

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

    event BorrowPositionOpen(address indexed _borrower, uint256 indexed _borrowAccountNumber);

    // ========================= Functions =========================

    /**
     *
     * @param  _fromAccountNumber   The index from which `msg.sender` will be sourcing the deposit
     * @param  _toAccountNumber     The index into which `msg.sender` will be depositing
     * @param  _collateralMarketId  The ID of the market being deposited
     * @param  _amountWei           The amount, in Wei, to deposit
     * @param  _balanceCheckFlag    Flag used to check if `_fromAccountNumber`, `_toAccountNumber`, or both accounts can
     *                              go negative after the transfer settles. Setting the flag to
     *                              `AccountBalanceLib.BalanceCheckFlag.None=3` results in neither account being
     *                              checked.
     */
    function openBorrowPosition(
        uint256 _fromAccountNumber,
        uint256 _toAccountNumber,
        uint256 _collateralMarketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     * @notice  This method can only be called once the user's debt has been reduced to zero. Sends all
     *          `_collateralMarketIds` from `_borrowAccountNumber` to `_toAccountNumber`.
     *
     * @param  _borrowAccountNumber The index from which `msg.sender` collateral will be withdrawn
     * @param  _toAccountNumber     The index into which `msg.sender` will be depositing leftover collateral
     * @param  _collateralMarketIds The IDs of the markets being withdrawn, to close the position
     */
    function closeBorrowPosition(
        uint256 _borrowAccountNumber,
        uint256 _toAccountNumber,
        uint256[] calldata _collateralMarketIds
    ) external;

    /**
     *
     * @param  _fromAccountNumber   The index from which `msg.sender` will be withdrawing assets
     * @param  _toAccountNumber     The index into which `msg.sender` will be depositing assets
     * @param  _marketId            The ID of the market being transferred
     * @param  _amountWei           The amount, in Wei, to transfer
     * @param  _balanceCheckFlag    Flag used to check if `_fromAccountNumber`, `_toAccountNumber`, or both accounts can
     *                              go negative after the transfer settles. Setting the flag to
     *                              `AccountBalanceLib.BalanceCheckFlag.None=3` results in neither account being
     *                              checked.
     */
    function transferBetweenAccounts(
        uint256 _fromAccountNumber,
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     *
     * @param  _fromAccountNumber   The index from which `msg.sender` will be depositing assets
     * @param  _borrowAccountNumber The index of the borrow position for that will receive the deposited assets
     * @param  _marketId            The ID of the market being transferred
     * @param  _balanceCheckFlag    Flag used to check if `_fromAccountNumber`, `_borrowAccountNumber`, or both accounts
     *                              can go negative after the transfer settles. Setting the flag to
     *                              `AccountBalanceLib.BalanceCheckFlag.None=3` results in neither account being
     *                              checked.
     */
    function repayAllForBorrowPosition(
        uint256 _fromAccountNumber,
        uint256 _borrowAccountNumber,
        uint256 _marketId,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;
}


interface IBorrowPositionProxyV2 is IAuthorizationBase, IBorrowPositionProxyV1 {

    // ========================= Functions =========================

    /**
     *
     * @param  _fromAccountOwner    The account from which the user will be sourcing the deposit
     * @param  _fromAccountNumber   The index from which `_toAccountOwner` will be sourcing the deposit
     * @param  _toAccountOwner      The account into which `_fromAccountOwner` will be depositing
     * @param  _toAccountNumber     The index into which `_fromAccountOwner` will be depositing
     * @param  _collateralMarketId  The ID of the market being deposited
     * @param  _amountWei           The amount, in Wei, to deposit
     * @param  _balanceCheckFlag    Flag used to check if `_fromAccountNumber`, `_toAccountNumber`, or both accounts can
     *                              go negative after the transfer settles. Setting the flag to
     *                              `AccountBalanceLib.BalanceCheckFlag.None=3` results in neither account being
     *                              checked.
     */
    function openBorrowPositionWithDifferentAccounts(
        address _fromAccountOwner,
        uint256 _fromAccountNumber,
        address _toAccountOwner,
        uint256 _toAccountNumber,
        uint256 _collateralMarketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     * @notice  This method can only be called once the user's debt has been reduced to zero. Sends all
     *          `_collateralMarketIds` from `_borrowAccountNumber` to `_toAccountNumber`.
     *
     * @param  _borrowAccountOwner  The account from which collateral will be withdrawn
     * @param  _borrowAccountNumber The index from which `msg.sender` collateral will be withdrawn
     * @param  _toAccountOwner      The account into which `_borrowAccountOwner` will be depositing leftover collateral
     * @param  _toAccountNumber     The index into which `_borrowAccountOwner` will be depositing leftover collateral
     * @param  _collateralMarketIds The IDs of the markets being withdrawn, to close the position
     */
    function closeBorrowPositionWithDifferentAccounts(
        address _borrowAccountOwner,
        uint256 _borrowAccountNumber,
        address _toAccountOwner,
        uint256 _toAccountNumber,
        uint256[] calldata _collateralMarketIds
    ) external;

    /**
     *
     * @param  _fromAccountOwner    The account from which assets will be withdrawn
     * @param  _fromAccountNumber   The index from which `msg.sender` will be withdrawing assets
     * @param  _toAccountOwner      The account to which assets will be deposited
     * @param  _toAccountNumber     The index into which `msg.sender` will be depositing assets
     * @param  _marketId            The ID of the market being transferred
     * @param  _amountWei           The amount, in Wei, to transfer
     * @param  _balanceCheckFlag    Flag used to check if `_fromAccountNumber`, `_toAccountNumber`, or both accounts can
     *                              go negative after the transfer settles. Setting the flag to
     *                              `AccountBalanceLib.BalanceCheckFlag.None=3` results in neither account being
     *                              checked.
     */
    function transferBetweenAccountsWithDifferentAccounts(
        address _fromAccountOwner,
        uint256 _fromAccountNumber,
        address _toAccountOwner,
        uint256 _toAccountNumber,
        uint256 _marketId,
        uint256 _amountWei,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;

    /**
     *
     * @param  _fromAccountOwner    The account from which assets will be withdrawn for repayment
     * @param  _fromAccountNumber   The index from which `msg.sender` will be depositing assets
     * @param  _borrowAccountOwner  The account of the borrow position that will receive the deposited assets
     * @param  _borrowAccountNumber The index of the borrow position for that will receive the deposited assets
     * @param  _marketId            The ID of the market being transferred
     * @param  _balanceCheckFlag    Flag used to check if `_fromAccountNumber`, `_borrowAccountNumber`, or both accounts
     *                              can go negative after the transfer settles. Setting the flag to
     *                              `AccountBalanceLib.BalanceCheckFlag.None=3` results in neither account being
     *                              checked.
     */
    function repayAllForBorrowPositionWithDifferentAccounts(
        address _fromAccountOwner,
        uint256 _fromAccountNumber,
        address _borrowAccountOwner,
        uint256 _borrowAccountNumber,
        uint256 _marketId,
        AccountBalanceLib.BalanceCheckFlag _balanceCheckFlag
    ) external;
}

File 22 of 27 : IAccessControlInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title Partial AccessControl interface needed by internal functions
 */
interface IAccessControlInternal {
    event RoleAdminChanged(
        bytes32 indexed role,
        bytes32 indexed previousAdminRole,
        bytes32 indexed newAdminRole
    );

    event RoleGranted(
        bytes32 indexed role,
        address indexed account,
        address indexed sender
    );

    event RoleRevoked(
        bytes32 indexed role,
        address indexed account,
        address indexed sender
    );
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title Set implementation with enumeration functions
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
library EnumerableSet {
    error EnumerableSet__IndexOutOfBounds();

    struct Set {
        bytes32[] _values;
        // 1-indexed to allow 0 to signify nonexistence
        mapping(bytes32 => uint256) _indexes;
    }

    struct Bytes32Set {
        Set _inner;
    }

    struct AddressSet {
        Set _inner;
    }

    struct UintSet {
        Set _inner;
    }

    function at(
        Bytes32Set storage set,
        uint256 index
    ) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    function at(
        AddressSet storage set,
        uint256 index
    ) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    function at(
        UintSet storage set,
        uint256 index
    ) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    function contains(
        Bytes32Set storage set,
        bytes32 value
    ) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    function contains(
        AddressSet storage set,
        address value
    ) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    function contains(
        UintSet storage set,
        uint256 value
    ) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    function indexOf(
        Bytes32Set storage set,
        bytes32 value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, value);
    }

    function indexOf(
        AddressSet storage set,
        address value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(uint256(uint160(value))));
    }

    function indexOf(
        UintSet storage set,
        uint256 value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(value));
    }

    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function add(
        Bytes32Set storage set,
        bytes32 value
    ) internal returns (bool) {
        return _add(set._inner, value);
    }

    function add(
        AddressSet storage set,
        address value
    ) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    function remove(
        Bytes32Set storage set,
        bytes32 value
    ) internal returns (bool) {
        return _remove(set._inner, value);
    }

    function remove(
        AddressSet storage set,
        address value
    ) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    function remove(
        UintSet storage set,
        uint256 value
    ) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    function toArray(
        Bytes32Set storage set
    ) internal view returns (bytes32[] memory) {
        return set._inner._values;
    }

    function toArray(
        AddressSet storage set
    ) internal view returns (address[] memory) {
        bytes32[] storage values = set._inner._values;
        address[] storage array;

        assembly {
            array.slot := values.slot
        }

        return array;
    }

    function toArray(
        UintSet storage set
    ) internal view returns (uint256[] memory) {
        bytes32[] storage values = set._inner._values;
        uint256[] storage array;

        assembly {
            array.slot := values.slot
        }

        return array;
    }

    function _at(
        Set storage set,
        uint256 index
    ) private view returns (bytes32) {
        if (index >= set._values.length)
            revert EnumerableSet__IndexOutOfBounds();
        return set._values[index];
    }

    function _contains(
        Set storage set,
        bytes32 value
    ) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    function _indexOf(
        Set storage set,
        bytes32 value
    ) private view returns (uint256) {
        unchecked {
            return set._indexes[value] - 1;
        }
    }

    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    function _add(
        Set storage set,
        bytes32 value
    ) private returns (bool status) {
        if (!_contains(set, value)) {
            set._values.push(value);
            set._indexes[value] = set._values.length;
            status = true;
        }
    }

    function _remove(
        Set storage set,
        bytes32 value
    ) private returns (bool status) {
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            unchecked {
                bytes32 last = set._values[set._values.length - 1];

                // move last value to now-vacant index

                set._values[valueIndex - 1] = last;
                set._indexes[last] = valueIndex;
            }
            // clear last index

            set._values.pop();
            delete set._indexes[value];

            status = true;
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { UintUtils } from './UintUtils.sol';

library AddressUtils {
    using UintUtils for uint256;

    error AddressUtils__InsufficientBalance();
    error AddressUtils__NotContract();
    error AddressUtils__SendValueFailed();

    function toString(address account) internal pure returns (string memory) {
        return uint256(uint160(account)).toHexString(20);
    }

    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    function sendValue(address payable account, uint256 amount) internal {
        (bool success, ) = account.call{ value: amount }('');
        if (!success) revert AddressUtils__SendValueFailed();
    }

    function functionCall(
        address target,
        bytes memory data
    ) internal returns (bytes memory) {
        return
            functionCall(target, data, 'AddressUtils: failed low-level call');
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory error
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, error);
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                'AddressUtils: failed low-level call with value'
            );
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) internal returns (bytes memory) {
        if (value > address(this).balance)
            revert AddressUtils__InsufficientBalance();
        return _functionCallWithValue(target, data, value, error);
    }

    /**
     * @notice execute arbitrary external call with limited gas usage and amount of copied return data
     * @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License)
     * @param target recipient of call
     * @param gasAmount gas allowance for call
     * @param value native token value to include in call
     * @param maxCopy maximum number of bytes to copy from return data
     * @param data encoded call data
     * @return success whether call is successful
     * @return returnData copied return data
     */
    function excessivelySafeCall(
        address target,
        uint256 gasAmount,
        uint256 value,
        uint16 maxCopy,
        bytes memory data
    ) internal returns (bool success, bytes memory returnData) {
        returnData = new bytes(maxCopy);

        assembly {
            // execute external call via assembly to avoid automatic copying of return data
            success := call(
                gasAmount,
                target,
                value,
                add(data, 0x20),
                mload(data),
                0,
                0
            )

            // determine whether to limit amount of data to copy
            let toCopy := returndatasize()

            if gt(toCopy, maxCopy) {
                toCopy := maxCopy
            }

            // store the length of the copied bytes
            mstore(returnData, toCopy)

            // copy the bytes from returndata[0:toCopy]
            returndatacopy(add(returnData, 0x20), 0, toCopy)
        }
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) private returns (bytes memory) {
        if (!isContract(target)) revert AddressUtils__NotContract();

        (bool success, bytes memory returnData) = target.call{ value: value }(
            data
        );

        if (success) {
            return returnData;
        } else if (returnData.length > 0) {
            assembly {
                let returnData_size := mload(returnData)
                revert(add(32, returnData), returnData_size)
            }
        } else {
            revert(error);
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title utility functions for uint256 operations
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
 */
library UintUtils {
    error UintUtils__InsufficientHexLength();

    bytes16 private constant HEX_SYMBOLS = '0123456789abcdef';

    function add(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? sub(a, -b) : a + uint256(b);
    }

    function sub(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? add(a, -b) : a - uint256(b);
    }

    function toString(uint256 value) internal pure returns (string memory) {
        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);
    }

    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return '0x00';
        }

        uint256 length = 0;

        for (uint256 temp = value; temp != 0; temp >>= 8) {
            unchecked {
                length++;
            }
        }

        return toHexString(value, 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';

        unchecked {
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
        }

        if (value != 0) revert UintUtils__InsufficientHexLength();

        return string(buffer);
    }
}

File 26 of 27 : AccessControlStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { EnumerableSet } from '../../data/EnumerableSet.sol';

library AccessControlStorage {
    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    struct Layout {
        mapping(bytes32 => RoleData) roles;
    }

    bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00;

    bytes32 internal constant STORAGE_SLOT =
        keccak256('solidstate.contracts.storage.AccessControl');

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

File 27 of 27 : ReentrancyGuardStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

library ReentrancyGuardStorage {
    struct Layout {
        uint256 status;
    }

    bytes32 internal constant STORAGE_SLOT =
        keccak256('solidstate.contracts.storage.ReentrancyGuard');

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

Settings
{
  "remappings": [
    "@axelar-network/=node_modules/@axelar-network/",
    "@chainlink/=node_modules/@chainlink/",
    "@eth-optimism/=node_modules/@eth-optimism/",
    "@layerzerolabs/=node_modules/@layerzerolabs/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@solidstate/=node_modules/@solidstate/",
    "ds-test/=node_modules/ds-test/",
    "forge-std/=node_modules/forge-std/",
    "hardhat-deploy/=node_modules/hardhat-deploy/",
    "solidity-bytes-utils/=node_modules/solidity-bytes-utils/",
    "solmate/=node_modules/solmate/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address[]","name":"targets","type":"address[]"},{"internalType":"address[]","name":"allowedTokens","type":"address[]"},{"internalType":"address[]","name":"allowedSpenders","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"UintUtils__InsufficientHexLength","type":"error"},{"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"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"claim","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":[{"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":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"renounceRole","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":"setFrozen","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"address","name":"target","type":"address"}],"name":"setSelector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

0x608060405234801562000010575f80fd5b5060405162002e7f38038062002e7f833981016040819052620000339162002256565b7fe16920893711b985df3893d85b39127b3a481e91c3c57640cd8970c06e9e6007804661a4b10362000e2f575f855f81518110620000755762000075620022e1565b60209081029190910181015163714ccf7b60e01b5f908152600186018352604080822080546001600160a01b0385166001600160a01b0319918216811790925563e1f21c6760e01b8452828420805482168317905563fde7a94160e01b84528284208054821683179055630f5af51760e11b845282842080548216831790556323b1a1f760e11b8452828420805482168317905563feaca58360e01b84528284208054821683179055633bf7365d60e21b8452828420805482168317905563024ece8960e01b84528284208054821683179055631e131fa360e11b845282842080548216831790556306fdde0360e01b84528284208054821683179055632cfc019d60e21b8452828420805482168317905563fbfa77cf60e01b8452828420805482168317905563cdf456e160e01b845282842080548216831790556303feff6960e21b845282842080548216831790556348d2a1db60e11b8452828420805482168317905563e070affd60e01b845282842080548216831790556336734af360e21b845282842080548216831790556313114a9d60e01b8452828420805482168317905563516a4d0560e01b84528284208054821683179055634ec8e34f60e01b845282842080549091169091179055805160c08101825260a081019283529182529281018890528083018790526702c68af0bb140000606082015266470de4df82000060808201529151909250620002db918391632d938a2360e21b91620002a2916024016200235e565b60408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200210816565b85600181518110620002f157620002f1620022e1565b60209081029190910181015163111ad34760e11b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563bc994c1b60e01b8552919093208054909116909217909155865190915086906002908110620003665762000366620022e1565b60209081029190910181015163c0517dc560e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b03199182168117909255636d3d407760e11b8552919093208054909116909217909155865190915086906003908110620003db57620003db620022e1565b60209081029190910181015163b911a26f60e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b031991821681179092556368865bc760e01b8552828520805482168317905563200e1c6960e21b8552919093208054909116909217909155865190915086906004908110620004655762000465620022e1565b602090810291909101810151631b8085e360e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563db42c44d60e01b855282852080548216831790556319dd283560e31b855282852080548216831790556317167f5f60e11b8552828520805482168317905563f846f74b60e01b8552828520805482168317905563094cbd3160e21b85529190932080549091169092179091558651909150869060059081106200052e576200052e620022e1565b602090810291909101810151636b7fff5160e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563079b57a960e21b8552828520805482168317905563288d863b60e11b855282852080548216831790556357615a6760e11b8552828520805482168317905563209ebd3f60e11b8552919093208054909116909217909155865190915086906006908110620005e257620005e2620022e1565b6020908102919091018101516261a91f60e11b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563686587fd60e01b855282852080548216831790556328c2aa2d60e11b85528285208054821683179055630a33f96f60e41b855282852080548216831790556333588b1160e01b85528285208054821683179055633a4edd8760e11b855282852080548216831790556307512e7f60e41b8552828520805482168317905563750ecd8160e01b85528285208054821683179055637f95af7360e01b855282852080548216831790556303abfae760e31b8552919093208054909116909217909155865190915086906007908110620006fe57620006fe620022e1565b602090810291909101810151631e1012e960e31b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563386189a960e01b8552828520805482168317905563089508ed60e11b85528285208054821683179055635ed81d6960e11b85528285208054821683179055631b8c3fdb60e31b85528285208054821683179055631fdb7b5760e11b85528285208054821683179055633dbf1f4160e21b8552828520805482168317905563be4a7e3f60e01b8552828520805482168317905563018c01f360e01b85528285208054821683179055636221afaf60e11b8552828520805482168317905563c28374ed60e01b85528285208054821683179055637f4d55d160e11b8552919093208054909116909217909155865190915086906008908110620008455762000845620022e1565b60209081029190910181015163771839f760e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563329ea2dd60e21b8552828520805482168317905563cb88548d60e01b8552828520805482168317905563df5619f760e01b8552828520805482168317905563794131d760e01b8552919093208054909116909217909155865190915086906009908110620008f957620008f9620022e1565b602090810291909101810151630b2331fb60e41b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563279c187f60e21b85528285208054821683179055632072e56560e21b855282852080548216831790556328b00bb360e21b855291909320805490911690921790915586519091508690600a908110620009985762000998620022e1565b602090810291909101810151637bfe4fbf60e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b03199182168117909255631f49a03560e21b85528285208054821683179055630c6ae1a560e41b85528285208054821683179055632479c21f60e01b8552828520805482168317905563e80df09b60e01b855282852080548216831790556301b2c27560e71b85528285208054821683179055631c68422b60e21b8552828520805482168317905563291a4e6d60e11b855282852080548216831790556370b8627360e01b85528285208054821683179055630a85bd0160e11b8552828520805482168317905563fe4ee96760e01b8552828520805482168317905563e2b36dc360e01b85528285208054821683179055631a33be3d60e11b855291909320805490911690921790915586519091508690600b90811062000af45762000af4620022e1565b602090810291909101810151630708c69360e21b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b031991821681179092556312223f5960e31b8552828520805482168317905563c194a75f60e01b8552828520805482168317905563b125088560e01b855291909320805490911690921790915586519091508690600c90811062000b935762000b93620022e1565b6020908102919091018101516356bc7a1360e11b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b03199182168117909255637f37b68d60e11b8552828520805482168317905563a861681f60e01b855291909320805490911690921790915586519091508690600d90811062000c1d5762000c1d620022e1565b60209081029190910181015163a339276560e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563d82b60df60e01b8552828520805482168317905563134f396760e11b85528285208054821683179055631586852560e11b85528285208054821683179055632d69ff6b60e21b855282852080548216831790556362ccb4d360e11b8552828520805482168317905563ceeb661960e01b855291909320805490911690921790915586519091508690600e90811062000cfb5762000cfb620022e1565b6020908102919091018101516384e4a75b60e01b5f90815260018601909252604090912080546001600160a01b0319166001600160a01b03831617905586519091508690600f90811062000d535762000d53620022e1565b602090810291909101810151633b6a43d760e01b5f90815260018601909252604080832080546001600160a01b03199081166001600160a01b0390941693841790915563150c8b3760e01b8452818420805482168417905563218025e160e21b84528184208054821684179055635fc484a160e01b8452818420805482168417905563127b603160e21b8452818420805482168417905563357f705160e01b8452818420805482168417905563a00906c760e01b8452818420805482168417905563becfb24f60e01b84529220805490921617905550620020fd565b46612105036200136c575f855f8151811062000e4f5762000e4f620022e1565b60209081029190910181015163714ccf7b60e01b5f908152600186018352604080822080546001600160a01b0385166001600160a01b0319918216811790925563e1f21c6760e01b8452828420805482168317905563fde7a94160e01b84528284208054821683179055630f5af51760e11b845282842080548216831790556323b1a1f760e11b8452828420805482168317905563feaca58360e01b84528284208054821683179055633bf7365d60e21b8452828420805482168317905563024ece8960e01b84528284208054821683179055631e131fa360e11b845282842080548216831790556306fdde0360e01b84528284208054821683179055632cfc019d60e21b8452828420805482168317905563fbfa77cf60e01b8452828420805482168317905563cdf456e160e01b845282842080548216831790556303feff6960e21b845282842080548216831790556348d2a1db60e11b8452828420805482168317905563e070affd60e01b845282842080548216831790556336734af360e21b845282842080548216831790556313114a9d60e01b8452828420805482168317905563516a4d0560e01b84528284208054821683179055634ec8e34f60e01b845282842080549091169091179055805160c08101825260a081019283529182529281018890528083018790526702c68af0bb140000606082015266470de4df820000608082015291519092506200107c918391632d938a2360e21b91620002a2916024016200235e565b85600181518110620010925762001092620022e1565b60209081029190910181015163111ad34760e11b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563bc994c1b60e01b8552919093208054909116909217909155865190915086906002908110620011075762001107620022e1565b60209081029190910181015163c0517dc560e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b03199182168117909255636d3d407760e11b85529190932080549091169092179091558651909150869060039081106200117c576200117c620022e1565b60209081029190910181015163b911a26f60e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b031991821681179092556368865bc760e01b8552828520805482168317905563200e1c6960e21b8552919093208054909116909217909155865190915086906004908110620012065762001206620022e1565b602090810291909101810151631b8085e360e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563db42c44d60e01b855282852080548216831790556319dd283560e31b855282852080548216831790556317167f5f60e11b8552828520805482168317905563f846f74b60e01b8552828520805482168317905563094cbd3160e21b8552919093208054909116909217909155865190915086906005908110620012cf57620012cf620022e1565b602090810291909101810151636b7fff5160e01b5f90815260018601909252604080832080546001600160a01b03199081166001600160a01b0390941693841790915563079b57a960e21b8452818420805482168417905563288d863b60e11b845281842080548216841790556357615a6760e11b8452818420805482168417905563209ebd3f60e11b84529220805490921617905550620020fd565b46620138de0362001a00575f855f815181106200138d576200138d620022e1565b60209081029190910181015163714ccf7b60e01b5f908152600186018352604080822080546001600160a01b0385166001600160a01b0319918216811790925563e1f21c6760e01b8452828420805482168317905563fde7a94160e01b84528284208054821683179055630f5af51760e11b845282842080548216831790556323b1a1f760e11b8452828420805482168317905563feaca58360e01b84528284208054821683179055633bf7365d60e21b8452828420805482168317905563024ece8960e01b84528284208054821683179055631e131fa360e11b845282842080548216831790556306fdde0360e01b84528284208054821683179055632cfc019d60e21b8452828420805482168317905563fbfa77cf60e01b8452828420805482168317905563cdf456e160e01b845282842080548216831790556303feff6960e21b845282842080548216831790556348d2a1db60e11b8452828420805482168317905563e070affd60e01b845282842080548216831790556336734af360e21b845282842080548216831790556313114a9d60e01b8452828420805482168317905563516a4d0560e01b84528284208054821683179055634ec8e34f60e01b845282842080549091169091179055805160c08101825260a081019283529182529281018890528083018790526702c68af0bb140000606082015266470de4df82000060808201529151909250620015ba918391632d938a2360e21b91620002a2916024016200235e565b85600181518110620015d057620015d0620022e1565b60209081029190910181015163111ad34760e11b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563bc994c1b60e01b8552919093208054909116909217909155865190915086906002908110620016455762001645620022e1565b60209081029190910181015163c0517dc560e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b03199182168117909255636d3d407760e11b8552919093208054909116909217909155865190915086906003908110620016ba57620016ba620022e1565b60209081029190910181015162c8935560e31b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b03199182168117909255635ed6253560e01b8552828520805482168317905563026c79b760e21b85528285208054821683179055631c22bea160e21b855282852080548216831790556330810ecf60e21b85528285208054821683179055635281c60360e01b855282852080548216831790556347b8a2f160e11b8552828520805482168317905562f3237560e61b8552828520805482168317905563790c5b9960e01b8552828520805482168317905563377b893b60e21b85528285208054821683179055630a093a8560e21b855282852080548216831790556321727ff760e21b85528285208054821683179055636c1d0f7d60e11b85528285208054821683179055630335417560e21b85528285208054821683179055630db8e03d60e41b855282852080548216831790556285b73960e31b8552828520805482168317905563066bae1560e31b85528285208054821683179055632cf0c3c960e21b8552828520805482168317905563be42cd0760e01b85528285208054821683179055631a12cc2960e31b85528285208054821683179055637591cb4560e11b8552828520805482168317905563cdaf6a8160e01b8552919093208054909116909217909155865190915086906004908110620018d057620018d0620022e1565b602090810291909101810151631e1012e960e31b5f90815260018601909252604080832080546001600160a01b03199081166001600160a01b0390941693841790915563386189a960e01b8452818420805482168417905563089508ed60e11b84528184208054821684179055635ed81d6960e11b84528184208054821684179055631b8c3fdb60e31b84528184208054821684179055631fdb7b5760e11b84528184208054821684179055633dbf1f4160e21b8452818420805482168417905563be4a7e3f60e01b8452818420805482168417905563018c01f360e01b84528184208054821684179055636221afaf60e11b8452818420805482168417905563c28374ed60e01b84528184208054821684179055637f4d55d160e11b84529220805490921617905550620020fd565b4660010362001a1f575f855f8151811062000e4f5762000e4f620022e1565b466103e7148062001a315750466103e6145b1562001e41575f855f8151811062001a4d5762001a4d620022e1565b60209081029190910181015163714ccf7b60e01b5f908152600186018352604080822080546001600160a01b0385166001600160a01b0319918216811790925563e1f21c6760e01b8452828420805482168317905563fde7a94160e01b84528284208054821683179055630f5af51760e11b845282842080548216831790556323b1a1f760e11b8452828420805482168317905563feaca58360e01b84528284208054821683179055633bf7365d60e21b8452828420805482168317905563024ece8960e01b84528284208054821683179055631e131fa360e11b845282842080548216831790556306fdde0360e01b84528284208054821683179055632cfc019d60e21b8452828420805482168317905563fbfa77cf60e01b8452828420805482168317905563cdf456e160e01b845282842080548216831790556303feff6960e21b845282842080548216831790556348d2a1db60e11b8452828420805482168317905563e070affd60e01b845282842080548216831790556336734af360e21b845282842080548216831790556313114a9d60e01b8452828420805482168317905563516a4d0560e01b84528284208054821683179055634ec8e34f60e01b845282842080549091169091179055805160c08101825260a081019283529182529281018890528083018790526702c68af0bb140000606082015266470de4df8200006080820152915190925062001c7a918391632d938a2360e21b91620002a2916024016200235e565b8560018151811062001c905762001c90620022e1565b60209081029190910181015163111ad34760e11b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563bc994c1b60e01b855291909320805490911690921790915586519091508690600290811062001d055762001d05620022e1565b60209081029190910181015163452a0b5b60e01b5f90815260018601909252604080832080546001600160a01b0384166001600160a01b0319918216811790925563e0072f1560e01b8552828520805482168317905563ba38fb5360e01b85528285208054821683179055637a569c0760e11b855282852080548216831790556361ee2c4360e01b8552828520805482168317905563175de87f60e01b8552828520805482168317905563c7b2a8c160e01b855291909320805490911690921790915586519091508690600390811062001de35762001de3620022e1565b60209081029190910181015163490530ff60e11b5f90815260018601909252604080832080546001600160a01b03199081166001600160a01b039094169384179091556336ebe1b960e21b84529220805490921617905550620020fd565b4661045c03620020fd575f855f8151811062001e615762001e61620022e1565b60209081029190910181015163714ccf7b60e01b5f908152600186018352604080822080546001600160a01b0385166001600160a01b0319918216811790925563e1f21c6760e01b8452828420805482168317905563fde7a94160e01b84528284208054821683179055630f5af51760e11b845282842080548216831790556323b1a1f760e11b8452828420805482168317905563feaca58360e01b84528284208054821683179055633bf7365d60e21b8452828420805482168317905563024ece8960e01b84528284208054821683179055631e131fa360e11b845282842080548216831790556306fdde0360e01b84528284208054821683179055632cfc019d60e21b8452828420805482168317905563fbfa77cf60e01b8452828420805482168317905563cdf456e160e01b845282842080548216831790556303feff6960e21b845282842080548216831790556348d2a1db60e11b8452828420805482168317905563e070affd60e01b845282842080548216831790556336734af360e21b845282842080548216831790556313114a9d60e01b8452828420805482168317905563516a4d0560e01b84528284208054821683179055634ec8e34f60e01b845282842080549091169091179055805160c08101825260a081019283529182529281018890528083018790526702c68af0bb140000606082015266470de4df820000608082015291519092506200208e918391632d938a2360e21b91620002a2916024016200235e565b85600181518110620020a457620020a4620022e1565b60209081029190910181015163111ad34760e11b5f90815260018601909252604080832080546001600160a01b03199081166001600160a01b0390941693841790915563bc994c1b60e01b845292208054909216179055505b505050505062002412565b5f826001600160a01b031682604051620021239190620023f5565b5f60405180830381855af49150503d805f81146200215d576040519150601f19603f3d011682016040523d82523d5f602084013e62002162565b606091505b505090508062002174573d5f803e3d5ffd5b505050565b634e487b7160e01b5f52604160045260245ffd5b80516001600160a01b0381168114620021a4575f80fd5b919050565b5f82601f830112620021b9575f80fd5b815160206001600160401b0380831115620021d857620021d862002179565b8260051b604051601f19603f8301168101818110848211171562002200576200220062002179565b604052938452602081870181019490810192508785111562002220575f80fd5b6020870191505b848210156200224b576200223b826200218d565b8352918301919083019062002227565b979650505050505050565b5f805f6060848603121562002269575f80fd5b83516001600160401b038082111562002280575f80fd5b6200228e87838801620021a9565b94506020860151915080821115620022a4575f80fd5b620022b287838801620021a9565b93506040860151915080821115620022c8575f80fd5b50620022d786828701620021a9565b9150509250925092565b634e487b7160e01b5f52603260045260245ffd5b5f5b8381101562002311578181015183820152602001620022f7565b50505f910152565b5f815180845260208085019450602084015f5b83811015620023535781516001600160a01b0316875295820195908201906001016200232c565b509495945050505050565b602081525f825160a0602084015280518060c0850152620023878160e0860160208501620022f5565b601f19915081601f82011684019050602085015160c0858303016040860152620023b560e083018262002319565b915050604085015182858303016060860152620023d3828262002319565b9250505060608401516080840152608084015160a08401528091505092915050565b5f825162002408818460208701620022f5565b9190910192915050565b610a5f80620024205f395ff3fe60806040526004361061007e575f3560e01c80638bb9c5bf1161004d5780638bb9c5bf146101c157806391d14854146101e0578063d547741f1461020f578063d9e305701461022e57610085565b8063215c7ded1461013d578063248a9ca31461015c5780632f2ff15d1461018e5780634e71d92d146101ad57610085565b3661008557005b5f80356001600160e01b03191681527fe16920893711b985df3893d85b39127b3a481e91c3c57640cd8970c06e9e600860205260409020545f80516020610a0a8339815191529081906001600160a01b03168061011d5760405162461bcd60e51b8152602060048201526011602482015270756e6b6e6f776e207369676e617475726560781b60448201526064015b60405180910390fd5b365f80375f80365f845af43d5f803e808015610137573d5ff35b3d5ffd5b005b348015610148575f80fd5b5061013b610157366004610830565b610242565b348015610167575f80fd5b5061017b61017636600461086f565b6102db565b6040519081526020015b60405180910390f35b348015610199575f80fd5b5061013b6101a8366004610886565b6102eb565b3480156101b8575f80fd5b5061013b61030c565b3480156101cc575f80fd5b5061013b6101db36600461086f565b61039b565b3480156101eb575f80fd5b506101ff6101fa366004610886565b6103a7565b6040519015158152602001610185565b34801561021a575f80fd5b5061013b610229366004610886565b6103b9565b348015610239575f80fd5b5061013b6103d5565b5f61024c816103fd565b5f80516020610a0a83398151915280548190610100900460ff161561029c5760405162461bcd60e51b8152602060048201526006602482015265333937bd32b760d11b6044820152606401610114565b506001600160e01b0319939093165f908152600190930160205250604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f6102e582610407565b92915050565b6102f482610407565b6102fd816103fd565b6103078383610427565b505050565b5f80516020610a0a8339815191528054819060ff16156103585760405162461bcd60e51b815260206004820152600760248201526618db185a5b595960ca1b6044820152606401610114565b815460ff1916600117825561036d5f33610427565b6103977fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e6333610427565b5050565b6103a481610486565b50565b5f6103b28383610490565b9392505050565b6103c282610407565b6103cb816103fd565b61030783836104b3565b5f6103df816103fd565b505f80516020610a0a833981519152805461ff001916610100179055565b6103a48133610512565b5f9081525f805160206109ea833981519152602052604090206002015490565b5f8281525f805160206109ea8339815191526020526040902061044a9082610574565b5060405133906001600160a01b0383169084907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d905f90a45050565b6103a481336104b3565b5f8281525f805160206109ea833981519152602052604081206103b29083610588565b5f8281525f805160206109ea833981519152602052604090206104d690826105a9565b5060405133906001600160a01b0383169084907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b905f90a45050565b61051c8282610490565b61039757610532816001600160a01b03166105bd565b61053d8360206105cf565b60405160200161054e9291906108c9565b60408051601f198184030181529082905262461bcd60e51b82526101149160040161093d565b5f6103b2836001600160a01b03841661071c565b6001600160a01b0381165f90815260018301602052604081205415156103b2565b5f6103b2836001600160a01b03841661075d565b60606102e56001600160a01b03831660145b60605f6105dd836002610983565b6105e890600261099a565b67ffffffffffffffff811115610600576106006109ad565b6040519080825280601f01601f19166020018201604052801561062a576020820181803683370190505b509050600360fc1b815f81518110610644576106446109c1565b60200101906001600160f81b03191690815f1a905350600f60fb1b81600181518110610672576106726109c1565b60200101906001600160f81b03191690815f1a905350600160028402015b60018111156106fc576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106106c1576106c16109c1565b1a60f81b8282815181106106d7576106d76109c1565b60200101906001600160f81b03191690815f1a90535060049490941c935f1901610690565b5083156103b25760405163c913478560e01b815260040160405180910390fd5b5f8181526001830160205260408120546102e557508154600180820184555f8481526020808220909301849055845493815293810190915260409092205590565b5f818152600183016020526040812054801561080e5783545f9085905f19810190811061078c5761078c6109c1565b905f5260205f200154905080855f0160018403815481106107af576107af6109c1565b5f9182526020808320909101929092559182526001860190526040902081905583548490806107e0576107e06109d5565b600190038181905f5260205f20015f90559055836001015f8481526020019081526020015f205f9055600191505b5092915050565b80356001600160a01b038116811461082b575f80fd5b919050565b5f8060408385031215610841575f80fd5b82356001600160e01b031981168114610858575f80fd5b915061086660208401610815565b90509250929050565b5f6020828403121561087f575f80fd5b5035919050565b5f8060408385031215610897575f80fd5b8235915061086660208401610815565b5f5b838110156108c15781810151838201526020016108a9565b50505f910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081525f83516109008160178501602088016108a7565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516109318160288401602088016108a7565b01602801949350505050565b602081525f825180602084015261095b8160408501602087016108a7565b601f01601f19169190910160400192915050565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176102e5576102e561096f565b808201808211156102e5576102e561096f565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52603160045260245ffdfed3889cc5458b268d0544e5534672df1296288ca3f93cbd39bd6f497a5c622811e16920893711b985df3893d85b39127b3a481e91c3c57640cd8970c06e9e6007a26469706673582212200988e3c74b37324cb883a5106a393ae0d5b796bf7685b8d389daee0098cf028064736f6c634300081800330000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000c2261dc253319cdc45fc2f32dc29e6bb128ca8120000000000000000000000002406aacbdf8463176deb285adaa81768415b6c7e00000000000000000000000002295e88bca3c6c1f51b40ef2f43ae31b23e13d70000000000000000000000007b55adc636594c60b5d356faa11f8ea55c88405700000000000000000000000000000000000000000000000000000000000000060000000000000000000000005555555555555555555555555555555555555555000000000000000000000000be6727b535545c67d5caa73dea54865b92cf79070000000000000000000000009fdbda0a5e284c32744d2f17ee5c74b284993463000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb000000000000000000000000068f321fa8fb9f0d135f290ef6a3e2813e1c8a29000000000000000000000000a18fdd2ec2bacaa73634e69a74044af1c89d73550000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a18fdd2ec2bacaa73634e69a74044af1c89d7355

Deployed Bytecode

0x60806040526004361061007e575f3560e01c80638bb9c5bf1161004d5780638bb9c5bf146101c157806391d14854146101e0578063d547741f1461020f578063d9e305701461022e57610085565b8063215c7ded1461013d578063248a9ca31461015c5780632f2ff15d1461018e5780634e71d92d146101ad57610085565b3661008557005b5f80356001600160e01b03191681527fe16920893711b985df3893d85b39127b3a481e91c3c57640cd8970c06e9e600860205260409020545f80516020610a0a8339815191529081906001600160a01b03168061011d5760405162461bcd60e51b8152602060048201526011602482015270756e6b6e6f776e207369676e617475726560781b60448201526064015b60405180910390fd5b365f80375f80365f845af43d5f803e808015610137573d5ff35b3d5ffd5b005b348015610148575f80fd5b5061013b610157366004610830565b610242565b348015610167575f80fd5b5061017b61017636600461086f565b6102db565b6040519081526020015b60405180910390f35b348015610199575f80fd5b5061013b6101a8366004610886565b6102eb565b3480156101b8575f80fd5b5061013b61030c565b3480156101cc575f80fd5b5061013b6101db36600461086f565b61039b565b3480156101eb575f80fd5b506101ff6101fa366004610886565b6103a7565b6040519015158152602001610185565b34801561021a575f80fd5b5061013b610229366004610886565b6103b9565b348015610239575f80fd5b5061013b6103d5565b5f61024c816103fd565b5f80516020610a0a83398151915280548190610100900460ff161561029c5760405162461bcd60e51b8152602060048201526006602482015265333937bd32b760d11b6044820152606401610114565b506001600160e01b0319939093165f908152600190930160205250604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f6102e582610407565b92915050565b6102f482610407565b6102fd816103fd565b6103078383610427565b505050565b5f80516020610a0a8339815191528054819060ff16156103585760405162461bcd60e51b815260206004820152600760248201526618db185a5b595960ca1b6044820152606401610114565b815460ff1916600117825561036d5f33610427565b6103977fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e6333610427565b5050565b6103a481610486565b50565b5f6103b28383610490565b9392505050565b6103c282610407565b6103cb816103fd565b61030783836104b3565b5f6103df816103fd565b505f80516020610a0a833981519152805461ff001916610100179055565b6103a48133610512565b5f9081525f805160206109ea833981519152602052604090206002015490565b5f8281525f805160206109ea8339815191526020526040902061044a9082610574565b5060405133906001600160a01b0383169084907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d905f90a45050565b6103a481336104b3565b5f8281525f805160206109ea833981519152602052604081206103b29083610588565b5f8281525f805160206109ea833981519152602052604090206104d690826105a9565b5060405133906001600160a01b0383169084907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b905f90a45050565b61051c8282610490565b61039757610532816001600160a01b03166105bd565b61053d8360206105cf565b60405160200161054e9291906108c9565b60408051601f198184030181529082905262461bcd60e51b82526101149160040161093d565b5f6103b2836001600160a01b03841661071c565b6001600160a01b0381165f90815260018301602052604081205415156103b2565b5f6103b2836001600160a01b03841661075d565b60606102e56001600160a01b03831660145b60605f6105dd836002610983565b6105e890600261099a565b67ffffffffffffffff811115610600576106006109ad565b6040519080825280601f01601f19166020018201604052801561062a576020820181803683370190505b509050600360fc1b815f81518110610644576106446109c1565b60200101906001600160f81b03191690815f1a905350600f60fb1b81600181518110610672576106726109c1565b60200101906001600160f81b03191690815f1a905350600160028402015b60018111156106fc576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106106c1576106c16109c1565b1a60f81b8282815181106106d7576106d76109c1565b60200101906001600160f81b03191690815f1a90535060049490941c935f1901610690565b5083156103b25760405163c913478560e01b815260040160405180910390fd5b5f8181526001830160205260408120546102e557508154600180820184555f8481526020808220909301849055845493815293810190915260409092205590565b5f818152600183016020526040812054801561080e5783545f9085905f19810190811061078c5761078c6109c1565b905f5260205f200154905080855f0160018403815481106107af576107af6109c1565b5f9182526020808320909101929092559182526001860190526040902081905583548490806107e0576107e06109d5565b600190038181905f5260205f20015f90559055836001015f8481526020019081526020015f205f9055600191505b5092915050565b80356001600160a01b038116811461082b575f80fd5b919050565b5f8060408385031215610841575f80fd5b82356001600160e01b031981168114610858575f80fd5b915061086660208401610815565b90509250929050565b5f6020828403121561087f575f80fd5b5035919050565b5f8060408385031215610897575f80fd5b8235915061086660208401610815565b5f5b838110156108c15781810151838201526020016108a9565b50505f910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081525f83516109008160178501602088016108a7565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516109318160288401602088016108a7565b01602801949350505050565b602081525f825180602084015261095b8160408501602087016108a7565b601f01601f19169190910160400192915050565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176102e5576102e561096f565b808201808211156102e5576102e561096f565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52603160045260245ffdfed3889cc5458b268d0544e5534672df1296288ca3f93cbd39bd6f497a5c622811e16920893711b985df3893d85b39127b3a481e91c3c57640cd8970c06e9e6007a26469706673582212200988e3c74b37324cb883a5106a393ae0d5b796bf7685b8d389daee0098cf028064736f6c63430008180033

Deployed Bytecode Sourcemap

1026:26162:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26589:15;26714:7;;-1:-1:-1;;;;;;26714:7:0;26702:20;;:11;:20;;;;;;-1:-1:-1;;;;;;;;;;;1221:21:0;;;-1:-1:-1;;;;;26702:20:0;;26732:78;;26772:27;;-1:-1:-1;;;26772:27:0;;216:2:27;26772:27:0;;;198:21:27;255:2;235:18;;;228:30;-1:-1:-1;;;274:18:27;;;267:47;331:18;;26772:27:0;;;;;;;;26732:78;26861:14;26858:1;26855;26842:34;26953:1;26950;26934:14;26931:1;26923:6;26916:5;26903:52;26989:16;26986:1;26983;26968:38;27026:6;27045:38;;;;27116:16;27113:1;27106:27;27045:38;27064:16;27061:1;27054:27;27019:116;;25991:257;;;;;;;;;;-1:-1:-1;25991:257:0;;;;;:::i;:::-;;:::i;864:111:17:-;;;;;;;;;;-1:-1:-1;864:111:17;;;;;:::i;:::-;;:::i;:::-;;;1234:25:27;;;1222:2;1207:18;864:111:17;;;;;;;;453:153;;;;;;;;;;-1:-1:-1;453:153:17;;;;;:::i;:::-;;:::i;26254:291:0:-;;;;;;;;;;;;;:::i;1242:81:17:-;;;;;;;;;;-1:-1:-1;1242:81:17;;;;;:::i;:::-;;:::i;662:146::-;;;;;;;;;;-1:-1:-1;662:146:17;;;;;:::i;:::-;;:::i;:::-;;;1694:14:27;;1687:22;1669:41;;1657:2;1642:18;662:146:17;1529:187:27;1031:155:17;;;;;;;;;;-1:-1:-1;1031:155:17;;;;;:::i;:::-;;:::i;25814:171:0:-;;;;;;;;;;;;;:::i;25991:257::-;26061:4;754:16:18;26061:4:0;754:10:18;:16::i;:::-;-1:-1:-1;;;;;;;;;;;26182:8:0;;1221:21;;26182:8:::1;::::0;::::1;;;26181:9;26173:28;;;::::0;-1:-1:-1;;;26173:28:0;;1923:2:27;26173:28:0::1;::::0;::::1;1905:21:27::0;1962:1;1942:18;;;1935:29;-1:-1:-1;;;1980:18:27;;;1973:36;2026:18;;26173:28:0::1;1721:329:27::0;26173:28:0::1;-1:-1:-1::0;;;;;;;26211:21:0;;;::::1;;::::0;;;:11:::1;::::0;;::::1;:21;::::0;-1:-1:-1;26211:21:0;;;;:30;;-1:-1:-1;;;;;;26211:30:0::1;-1:-1:-1::0;;;;;26211:30:0;;::::1;::::0;;;::::1;::::0;;25991:257::o;864:111:17:-;923:7;949:19;963:4;949:13;:19::i;:::-;942:26;864:111;-1:-1:-1;;864:111:17:o;453:153::-;543:19;557:4;543:13;:19::i;:::-;754:16:18;765:4;754:10;:16::i;:::-;574:25:17::1;585:4;591:7;574:10;:25::i;:::-;453:153:::0;;;:::o;26254:291:0:-;-1:-1:-1;;;;;;;;;;;26393:9:0;;1221:21;;26393:9;;26392:10;26384:30;;;;-1:-1:-1;;;26384:30:0;;2257:2:27;26384:30:0;;;2239:21:27;2296:1;2276:18;;;2269:29;-1:-1:-1;;;2314:18:27;;;2307:37;2361:18;;26384:30:0;2055:330:27;26384:30:0;26424:16;;-1:-1:-1;;26424:16:0;26436:4;26424:16;;;26450:28;26424:9;26467:10;26450;:28::i;:::-;26488:50;26499:26;26527:10;26488;:50::i;:::-;26278:267;;26254:291::o;1242:81:17:-;1297:19;1311:4;1297:13;:19::i;:::-;1242:81;:::o;662:146::-;755:4;778:23;787:4;793:7;778:8;:23::i;:::-;771:30;662:146;-1:-1:-1;;;662:146:17:o;1031:155::-;1122:19;1136:4;1122:13;:19::i;:::-;754:16:18;765:4;754:10;:16::i;:::-;1153:26:17::1;1165:4;1171:7;1153:11;:26::i;25814:171:0:-:0;25851:4;754:16:18;25851:4:0;754:10:18;:16::i;:::-;-1:-1:-1;;;;;;;;;;;;25963:15:0;;-1:-1:-1;;25963:15:0::1;;;::::0;;25814:171::o;1312:101:18:-;1378:28;1389:4;1395:10;1378;:28::i;2151:166::-;2233:7;2259:41;;;-1:-1:-1;;;;;;;;;;;2259:41:18;;;;;:51;;;;2151:166::o;2877:200::-;2955:35;:41;;;-1:-1:-1;;;;;;;;;;;2955:41:18;;;;;:62;;3009:7;2955:53;:62::i;:::-;-1:-1:-1;3032:38:18;;3059:10;;-1:-1:-1;;;;;3032:38:18;;;3044:4;;3032:38;;;;;2877:200;;:::o;3498:100::-;3562:29;3574:4;3580:10;3562:11;:29::i;989:211::-;1091:4;1126:41;;;-1:-1:-1;;;;;;;;;;;1126:41:18;;;;;:67;;1185:7;1126:58;:67::i;3203:204::-;3282:35;:41;;;-1:-1:-1;;;;;;;;;;;3282:41:18;;;;;:65;;3339:7;3282:56;:65::i;:::-;-1:-1:-1;3362:38:18;;3389:10;;-1:-1:-1;;;;;3362:38:18;;;3374:4;;3362:38;;;;;3203:204;;:::o;1563:461::-;1651:23;1660:4;1666:7;1651:8;:23::i;:::-;1646:372;;1835:18;:7;-1:-1:-1;;;;;1835:16:18;;:18::i;:::-;1924:29;1932:4;1950:2;1924:25;:29::i;:::-;1742:233;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;1742:233:18;;;;;;;;;;-1:-1:-1;;;1690:317:18;;;;;;;:::i;2669:172:22:-;2761:4;2784:50;2789:3;-1:-1:-1;;;;;2809:23:22;;2784:4;:50::i;1259:187::-;-1:-1:-1;;;;;1414:23:22;;1361:4;4562:19;;;:12;;;:19;;;;;;:24;;1384:55;4444:149;3139:178;3234:4;3257:53;3265:3;-1:-1:-1;;;;;3285:23:22;;3257:7;:53::i;295:138:23:-;353:13;385:41;-1:-1:-1;;;;;385:25:23;;423:2;1489:513:26;1586:13;1611:19;1643:10;1647:6;1643:1;:10;:::i;:::-;:14;;1656:1;1643:14;:::i;:::-;1633:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1633:25:26;;1611:47;;-1:-1:-1;;;1668:6:26;1675:1;1668:9;;;;;;;;:::i;:::-;;;;:15;-1:-1:-1;;;;;1668:15:26;;;;;;;;;-1:-1:-1;;;1693:6:26;1700:1;1693:9;;;;;;;;:::i;:::-;;;;:15;-1:-1:-1;;;;;1693:15:26;;;;;;;;-1:-1:-1;1773:1:26;1760;:10;;:14;1743:143;1780:1;1776;:5;1743:143;;;-1:-1:-1;;;1830:5:26;1838:3;1830:11;1818:24;;;;;;;:::i;:::-;;;;1806:6;1813:1;1806:9;;;;;;;;:::i;:::-;;;;:36;-1:-1:-1;;;;;1806:36:26;;;;;;;;-1:-1:-1;1870:1:26;1860:11;;;;;-1:-1:-1;;1783:3:26;1743:143;;;-1:-1:-1;1910:10:26;;1906:57;;1929:34;;-1:-1:-1;;;1929:34:26;;;;;;;;;;;4902:271:22;4987:11;4562:19;;;:12;;;:19;;;;;;5010:157;;-1:-1:-1;5052:23:22;;;;;;;;-1:-1:-1;5052:23:22;;;;;;;;;;;;;5111:18;;5089:19;;;:12;;;:19;;;;;;;:40;5052:23;4902:271::o;5179:596::-;5267:11;5311:19;;;:12;;;:19;;;;;;5345:15;;5341:428;;5431:18;;5404:12;;5419:3;;-1:-1:-1;;5431:22:22;;;5419:35;;;;;;:::i;:::-;;;;;;;;;5404:50;;5559:4;5529:3;:11;;5554:1;5541:10;:14;5529:27;;;;;;;;:::i;:::-;;;;;;;;;;;;:34;;;;5581:18;;;:12;;;:18;;;;;:31;;;5673:17;;5581:3;;5673:17;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;5711:3;:12;;:19;5724:5;5711:19;;;;;;;;;;;5704:26;;;5754:4;5745:13;;5341:428;5280:495;5179:596;;;;:::o;360:173:27:-;428:20;;-1:-1:-1;;;;;477:31:27;;467:42;;457:70;;523:1;520;513:12;457:70;360:173;;;:::o;538:360::-;605:6;613;666:2;654:9;645:7;641:23;637:32;634:52;;;682:1;679;672:12;634:52;708:23;;-1:-1:-1;;;;;;760:32:27;;750:43;;740:71;;807:1;804;797:12;740:71;830:5;-1:-1:-1;854:38:27;888:2;873:18;;854:38;:::i;:::-;844:48;;538:360;;;;;:::o;903:180::-;962:6;1015:2;1003:9;994:7;990:23;986:32;983:52;;;1031:1;1028;1021:12;983:52;-1:-1:-1;1054:23:27;;903:180;-1:-1:-1;903:180:27:o;1270:254::-;1338:6;1346;1399:2;1387:9;1378:7;1374:23;1370:32;1367:52;;;1415:1;1412;1405:12;1367:52;1451:9;1438:23;1428:33;;1480:38;1514:2;1503:9;1499:18;1480:38;:::i;2390:250::-;2475:1;2485:113;2499:6;2496:1;2493:13;2485:113;;;2575:11;;;2569:18;2556:11;;;2549:39;2521:2;2514:10;2485:113;;;-1:-1:-1;;2632:1:27;2614:16;;2607:27;2390:250::o;2645:812::-;3056:25;3051:3;3044:38;3026:3;3111:6;3105:13;3127:75;3195:6;3190:2;3185:3;3181:12;3174:4;3166:6;3162:17;3127:75;:::i;:::-;-1:-1:-1;;;3261:2:27;3221:16;;;3253:11;;;3246:40;3311:13;;3333:76;3311:13;3395:2;3387:11;;3380:4;3368:17;;3333:76;:::i;:::-;3429:17;3448:2;3425:26;;2645:812;-1:-1:-1;;;;2645:812:27:o;3462:396::-;3611:2;3600:9;3593:21;3574:4;3643:6;3637:13;3686:6;3681:2;3670:9;3666:18;3659:34;3702:79;3774:6;3769:2;3758:9;3754:18;3749:2;3741:6;3737:15;3702:79;:::i;:::-;3842:2;3821:15;-1:-1:-1;;3817:29:27;3802:45;;;;3849:2;3798:54;;3462:396;-1:-1:-1;;3462:396:27:o;3863:127::-;3924:10;3919:3;3915:20;3912:1;3905:31;3955:4;3952:1;3945:15;3979:4;3976:1;3969:15;3995:168;4068:9;;;4099;;4116:15;;;4110:22;;4096:37;4086:71;;4137:18;;:::i;4168:125::-;4233:9;;;4254:10;;;4251:36;;;4267:18;;:::i;4298:127::-;4359:10;4354:3;4350:20;4347:1;4340:31;4390:4;4387:1;4380:15;4414:4;4411:1;4404:15;4430:127;4491:10;4486:3;4482:20;4479:1;4472:31;4522:4;4519:1;4512:15;4546:4;4543:1;4536:15;4562:127;4623:10;4618:3;4614:20;4611:1;4604:31;4654:4;4651:1;4644:15;4678:4;4675:1;4668:15

Swarm Source

ipfs://0988e3c74b37324cb883a5106a393ae0d5b796bf7685b8d389daee0098cf0280

Block Transaction Gas Used Reward
view all blocks ##produced##

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

Validator Index Block Amount
View All Withdrawals

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

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