creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/strategies/SimpleFarmStrategy.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport { Sickle } from \"../Sickle.sol\";\nimport { ConnectorRegistry } from \"../ConnectorRegistry.sol\";\nimport {\n    TransferModule,\n    SickleFactory,\n    FeesLib\n} from \"./modules/TransferModule.sol\";\nimport { IFarmConnector } from \"../interfaces/IFarmConnector.sol\";\n\nlibrary SimpleFarmStrategyFees {\n    bytes4 constant Harvest = bytes4(keccak256(\"SimpleFarmHarvestFee\"));\n}\n\ncontract SimpleFarmStrategy is TransferModule {\n    ConnectorRegistry immutable connectorRegistry;\n\n    constructor(\n        SickleFactory factory,\n        FeesLib feesLib,\n        address wrappedNativeAddress,\n        ConnectorRegistry connectorRegistry_\n    ) TransferModule(factory, feesLib, wrappedNativeAddress) {\n        connectorRegistry = connectorRegistry_;\n    }\n\n    struct DepositParams {\n        address lpToken;\n        uint256 amountIn;\n        address stakingContractAddress;\n        bytes extraData;\n    }\n\n    struct WithdrawParams {\n        address lpToken;\n        uint256 amountOut;\n        address stakingContractAddress;\n        bytes extraData;\n    }\n\n    struct HarvestParams {\n        address[] tokensOut;\n        address stakingContractAddress;\n        bytes extraData;\n    }\n\n    function deposit(\n        DepositParams calldata params,\n        address approved,\n        bytes32 referralCode\n    ) public payable {\n        Sickle sickle = Sickle(\n            payable(factory.getOrDeploy(msg.sender, approved, referralCode))\n        );\n        address[] memory targets = new address[](2);\n        bytes[] memory data = new bytes[](2);\n\n        targets[0] = address(this);\n        data[0] = abi.encodeCall(\n            this._sickle_transfer_token_from_user,\n            (params.lpToken, params.amountIn, address(this), bytes4(0))\n        );\n\n        targets[1] =\n            connectorRegistry.connectorOf(params.stakingContractAddress);\n        data[1] = abi.encodeCall(\n            IFarmConnector.deposit,\n            (params.stakingContractAddress, params.lpToken, params.extraData)\n        );\n\n        sickle.multicall{ value: msg.value }(targets, data);\n    }\n\n    function withdraw(WithdrawParams calldata params) public {\n        Sickle sickle = Sickle(payable(factory.sickles(msg.sender)));\n        if (address(sickle) == address(0)) {\n            revert SickleNotDeployed();\n        }\n\n        address[] memory targets = new address[](2);\n        bytes[] memory data = new bytes[](2);\n\n        address farmConnector =\n            connectorRegistry.connectorOf(params.stakingContractAddress);\n        targets[0] = farmConnector;\n        data[0] = abi.encodeCall(\n            IFarmConnector.withdraw,\n            (params.stakingContractAddress, params.amountOut, params.extraData)\n        );\n\n        targets[1] = address(this);\n        data[1] = abi.encodeCall(\n            this._sickle_transfer_token_to_user, (params.lpToken)\n        );\n\n        sickle.multicall(targets, data);\n    }\n\n    function harvest(HarvestParams calldata params) public {\n        Sickle sickle = Sickle(payable(factory.sickles(msg.sender)));\n        if (address(sickle) == address(0)) {\n            revert SickleNotDeployed();\n        }\n\n        address[] memory targets = new address[](3);\n        bytes[] memory data = new bytes[](3);\n\n        address farmConnector =\n            connectorRegistry.connectorOf(params.stakingContractAddress);\n        targets[0] = farmConnector;\n        data[0] = abi.encodeCall(\n            IFarmConnector.claim,\n            (params.stakingContractAddress, params.extraData)\n        );\n\n        targets[1] = address(this);\n        data[1] = abi.encodeCall(\n            this._sickle_charge_fees,\n            (address(this), SimpleFarmStrategyFees.Harvest, params.tokensOut)\n        );\n\n        targets[2] = address(this);\n        data[2] = abi.encodeCall(\n            this._sickle_transfer_tokens_to_user, (params.tokensOut)\n        );\n\n        sickle.multicall(targets, data);\n    }\n\n    function exit(\n        HarvestParams calldata harvestParams,\n        WithdrawParams calldata withdrawParams\n    ) external {\n        harvest(harvestParams);\n        withdraw(withdrawParams);\n    }\n}\n"
      },
      "contracts/Sickle.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"./base/SickleStorage.sol\";\nimport \"./base/Multicall.sol\";\n\n/// @title Sickle contract\n/// @author vfat.tools\n/// @notice Sickle facilitates farming and interactions with MasterChef\n/// contracts\n/// @dev Base contract inheriting from all the other \"manager\" contracts\ncontract Sickle is SickleStorage, Multicall {\n    /// @notice Function to receive ETH\n    receive() external payable { }\n\n    /// @param sickleRegistry_ Address of the SickleRegistry contract\n    constructor(SickleRegistry sickleRegistry_)\n        initializer\n        Multicall(sickleRegistry_)\n    {\n        _Sickle_initialize(address(0), address(0));\n    }\n\n    /// @param sickleOwner_ Address of the Sickle owner\n    function initialize(\n        address sickleOwner_,\n        address approved_\n    ) external initializer {\n        _Sickle_initialize(sickleOwner_, approved_);\n    }\n\n    /// INTERNALS ///\n\n    function _Sickle_initialize(\n        address sickleOwner_,\n        address approved_\n    ) internal {\n        SickleStorage._SickleStorage_initialize(sickleOwner_, approved_);\n    }\n\n    function onERC721Received(\n        address, // operator\n        address, // from\n        uint256, // tokenId\n        bytes calldata // data\n    ) external pure returns (bytes4) {\n        return this.onERC721Received.selector;\n    }\n\n    function onERC1155Received(\n        address, // operator\n        address, // from\n        uint256, // id\n        uint256, // value\n        bytes calldata // data\n    ) external pure returns (bytes4) {\n        return this.onERC1155Received.selector;\n    }\n\n    function onERC1155BatchReceived(\n        address, // operator\n        address, // from\n        uint256[] calldata, // ids\n        uint256[] calldata, // values\n        bytes calldata // data\n    ) external pure returns (bytes4) {\n        return this.onERC1155BatchReceived.selector;\n    }\n}\n"
      },
      "contracts/ConnectorRegistry.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"./base/Admin.sol\";\nimport \"./base/TimelockAdmin.sol\";\n\nerror ConnectorNotRegistered(address target);\n\ninterface ICustomConnectorRegistry {\n    function connectorOf(address target) external view returns (address);\n}\n\ncontract ConnectorRegistry is Admin, TimelockAdmin {\n    event ConnectorChanged(address target, address connector);\n    event CustomRegistryAdded(address registry);\n    event CustomRegistryRemoved(address registry);\n\n    error ConnectorAlreadySet(address target);\n    error ConnectorNotSet(address target);\n\n    ICustomConnectorRegistry[] public customRegistries;\n    mapping(ICustomConnectorRegistry => bool) public isCustomRegistry;\n\n    mapping(address target => address connector) private connectors_;\n\n    constructor(\n        address admin_,\n        address timelockAdmin_\n    ) Admin(admin_) TimelockAdmin(timelockAdmin_) { }\n\n    /// @notice Update connector addresses for a batch of targets.\n    /// @dev Controls which connector contracts are used for the specified\n    /// targets.\n    /// @custom:access Restricted to protocol admin.\n    function setConnectors(\n        address[] calldata targets,\n        address[] calldata connectors\n    ) external onlyAdmin {\n        for (uint256 i; i != targets.length;) {\n            if (connectors_[targets[i]] != address(0)) {\n                revert ConnectorAlreadySet(targets[i]);\n            }\n            connectors_[targets[i]] = connectors[i];\n            emit ConnectorChanged(targets[i], connectors[i]);\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    function updateConnectors(\n        address[] calldata targets,\n        address[] calldata connectors\n    ) external onlyTimelockAdmin {\n        for (uint256 i; i != targets.length;) {\n            if (connectors_[targets[i]] == address(0)) {\n                revert ConnectorNotSet(targets[i]);\n            }\n            connectors_[targets[i]] = connectors[i];\n            emit ConnectorChanged(targets[i], connectors[i]);\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /// @notice Append an address to the custom registries list.\n    /// @custom:access Restricted to protocol admin.\n    function addCustomRegistry(ICustomConnectorRegistry registry)\n        external\n        onlyAdmin\n    {\n        customRegistries.push(registry);\n        isCustomRegistry[registry] = true;\n        emit CustomRegistryAdded(address(registry));\n    }\n\n    /// @notice Replace an address in the custom registries list.\n    /// @custom:access Restricted to protocol admin.\n    function updateCustomRegistry(\n        uint256 index,\n        ICustomConnectorRegistry newRegistry\n    ) external onlyTimelockAdmin {\n        address oldRegistry = address(customRegistries[index]);\n        isCustomRegistry[customRegistries[index]] = false;\n        emit CustomRegistryRemoved(oldRegistry);\n        customRegistries[index] = newRegistry;\n        isCustomRegistry[newRegistry] = true;\n        if (address(newRegistry) != address(0)) {\n            emit CustomRegistryAdded(address(newRegistry));\n        }\n    }\n\n    function connectorOf(address target) external view returns (address) {\n        address connector = connectors_[target];\n        if (connector != address(0)) {\n            return connector;\n        }\n\n        uint256 length = customRegistries.length;\n        for (uint256 i; i != length;) {\n            if (address(customRegistries[i]) != address(0)) {\n                try customRegistries[i].connectorOf(target) returns (\n                    address _connector\n                ) {\n                    if (_connector != address(0)) {\n                        return _connector;\n                    }\n                } catch {\n                    // Ignore\n                }\n            }\n\n            unchecked {\n                ++i;\n            }\n        }\n\n        revert ConnectorNotRegistered(target);\n    }\n\n    function hasConnector(address target) external view returns (bool) {\n        if (connectors_[target] != address(0)) {\n            return true;\n        }\n\n        uint256 length = customRegistries.length;\n        for (uint256 i; i != length;) {\n            if (address(customRegistries[i]) != address(0)) {\n                try customRegistries[i].connectorOf(target) returns (\n                    address _connector\n                ) {\n                    if (_connector != address(0)) {\n                        return true;\n                    }\n                } catch {\n                    // Ignore\n                }\n\n                unchecked {\n                    ++i;\n                }\n            }\n        }\n\n        return false;\n    }\n}\n"
      },
      "contracts/strategies/modules/TransferModule.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"./AccessControlModule.sol\";\nimport \"./FeesModule.sol\";\nimport \"./MsgValueModule.sol\";\n\ncontract TransferModule is AccessControlModule, FeesModule, MsgValueModule {\n    error ArrayLengthMismatch();\n    error TokenInRequired();\n\n    constructor(\n        SickleFactory factory_,\n        FeesLib feesLib_,\n        address wrappedNativeAddress_\n    )\n        FeesModule(feesLib_, wrappedNativeAddress_)\n        AccessControlModule(factory_)\n    { }\n\n    /// @dev Transfers the balance of {token} from the contract to the\n    /// sickle owner\n    /// @param token Address of the token to transfer\n    function _sickle_transfer_token_to_user(address token)\n        public\n        payable\n        onlyRegisteredSickle\n    {\n        address recipient = Sickle(payable(address(this))).owner();\n        if (token == address(0)) {\n            return;\n        }\n        if (token == ETH) {\n            IWETH9(wrappedNativeAddress).withdraw(\n                IWETH9(wrappedNativeAddress).balanceOf(address(this))\n            );\n            SafeTransferLib.safeTransferETH(recipient, address(this).balance);\n        } else if (token == wrappedNativeAddress) {\n            IWETH9(wrappedNativeAddress).withdraw(\n                IWETH9(wrappedNativeAddress).balanceOf(address(this))\n            );\n            SafeTransferLib.safeTransferETH(recipient, address(this).balance);\n        } else {\n            SafeTransferLib.safeTransfer(\n                token, recipient, IERC20(token).balanceOf(address(this))\n            );\n        }\n    }\n\n    /// @dev Transfers all balances of {tokens} and/or ETH from the contract\n    /// to the sickle owner\n    /// @param tokens An array of token addresses\n    function _sickle_transfer_tokens_to_user(address[] memory tokens)\n        external\n        payable\n        onlyRegisteredSickle\n    {\n        for (uint256 i = 0; i != tokens.length; i++) {\n            _sickle_transfer_token_to_user(tokens[i]);\n        }\n    }\n\n    /// @dev Transfers {amountIn} of {tokenIn} from the user to the Sickle\n    /// contract, charging the fees and converting the amount to WETH if\n    /// necessary\n    /// @param tokenIn Address of the token to transfer\n    /// @param amountIn Amount of the token to transfer\n    /// @param strategy Address of the caller strategy\n    /// @param feeSelector Selector of the caller function\n    function _sickle_transfer_token_from_user(\n        address tokenIn,\n        uint256 amountIn,\n        address strategy,\n        bytes4 feeSelector\n    ) public payable onlyRegisteredSickle {\n        _checkMsgValue(amountIn, tokenIn == ETH);\n\n        if (tokenIn != ETH) {\n            SafeTransferLib.safeTransferFrom(\n                tokenIn,\n                Sickle(payable(address(this))).owner(),\n                address(this),\n                amountIn\n            );\n        }\n\n        amountIn = _charge_fees(\n            keccak256(abi.encodePacked(strategy, feeSelector)),\n            tokenIn,\n            amountIn\n        );\n\n        if (tokenIn == ETH) {\n            IWETH9 weth = IWETH9(wrappedNativeAddress);\n            weth.deposit{ value: amountIn }();\n        }\n    }\n\n    /// @dev Transfers {amountIn} of {tokenIn} from the user to the Sickle\n    /// contract, charging the fees and converting the amount to WETH if\n    /// necessary\n    /// @param tokensIn Addresses of the tokens to transfer\n    /// @param amountsIn Amounts of the tokens to transfer\n    /// @param strategy Address of the caller strategy\n    /// @param feeSelector Selector of the caller function\n    function _sickle_transfer_tokens_from_user(\n        address[] memory tokensIn,\n        uint256[] memory amountsIn,\n        address strategy,\n        bytes4 feeSelector\n    ) external payable onlyRegisteredSickle {\n        if (tokensIn.length != amountsIn.length) {\n            revert ArrayLengthMismatch();\n        }\n        if (tokensIn.length == 0) {\n            revert TokenInRequired();\n        }\n\n        for (uint256 i = 0; i < tokensIn.length; i++) {\n            _sickle_transfer_token_from_user(\n                tokensIn[i], amountsIn[i], strategy, feeSelector\n            );\n        }\n    }\n}\n"
      },
      "contracts/interfaces/IFarmConnector.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IFarmConnector {\n    function deposit(\n        address target,\n        address token,\n        bytes memory extraData\n    ) external payable;\n\n    function withdraw(\n        address target,\n        uint256 amount,\n        bytes memory extraData\n    ) external;\n\n    function claim(address target, bytes memory extraData) external;\n}\n"
      },
      "contracts/base/SickleStorage.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/proxy/utils/Initializable.sol\";\n\nlibrary SickleStorageEvents {\n    event ApprovedAddressChanged(address newApproved);\n}\n\n/// @title SickleStorage contract\n/// @author vfat.tools\n/// @notice Base storage of the Sickle contract\n/// @dev This contract needs to be inherited by stub contracts meant to be used\n/// with `delegatecall`\nabstract contract SickleStorage is Initializable {\n    /// ERRORS ///\n\n    /// @notice Thrown when the caller is not the owner of the Sickle contract\n    error NotOwnerError(); // 0x74a21527\n\n    /// @notice Thrown when the caller is not a strategy contract or the\n    /// Flashloan Stub\n    error NotStrategyError(); // 0x4581ba62\n\n    /// STORAGE ///\n\n    /// @notice Address of the owner\n    address public owner;\n\n    /// @notice An address that can be set by the owner of the Sickle contract\n    /// in order to trigger specific functions.\n    address public approved;\n\n    /// MODIFIERS ///\n\n    /// @dev Restricts a function call to the owner, however if the admin was\n    /// not set yet,\n    /// the modifier will not restrict the call, this allows the SickleFactory\n    /// to perform\n    /// some calls on the user's behalf before passing the admin rights to them\n    modifier onlyOwner() {\n        if (msg.sender != owner) revert NotOwnerError();\n        _;\n    }\n\n    /// INITIALIZATION ///\n\n    /// @param owner_ Address of the owner of this Sickle contract\n    function _SickleStorage_initialize(\n        address owner_,\n        address approved_\n    ) internal onlyInitializing {\n        owner = owner_;\n        approved = approved_;\n    }\n\n    /// WRITE FUNCTIONS ///\n\n    /// @notice Sets the approved address of this Sickle\n    /// @param newApproved Address meant to be approved by the owner\n    function setApproved(address newApproved) external onlyOwner {\n        approved = newApproved;\n        emit SickleStorageEvents.ApprovedAddressChanged(newApproved);\n    }\n\n    /// @notice Checks if `caller` is either the owner of the Sickle contract\n    /// or was approved by them\n    /// @param caller Address to check\n    /// @return True if `caller` is either the owner of the Sickle contract\n    function isOwnerOrApproved(address caller) public view returns (bool) {\n        return caller == owner || caller == approved;\n    }\n}\n"
      },
      "contracts/base/Multicall.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"../base/SickleStorage.sol\";\nimport \"../SickleRegistry.sol\";\n\n/// @title Multicall contract\n/// @author vfat.tools\n/// @notice Enables calling multiple methods in a single call to the contract\nabstract contract Multicall is SickleStorage {\n    /// ERRORS ///\n\n    error MulticallParamsMismatchError(); // 0xc1e637c9\n\n    /// @notice Thrown when the target contract is not whitelisted\n    /// @param target Address of the non-whitelisted target\n    error TargetNotWhitelisted(address target); // 0x47ccabe7\n\n    /// @notice Thrown when the caller is not whitelisted\n    /// @param caller Address of the non-whitelisted caller\n    error CallerNotWhitelisted(address caller); // 0x252c8273\n\n    /// STORAGE ///\n\n    /// @notice Address of the SickleRegistry contract\n    /// @dev Needs to be immutable so that it's accessible for Sickle proxies\n    SickleRegistry public immutable registry;\n\n    /// INITIALIZATION ///\n\n    /// @param registry_ Address of the SickleRegistry contract\n    constructor(SickleRegistry registry_) initializer {\n        registry = registry_;\n    }\n\n    /// WRITE FUNCTIONS ///\n\n    /// @notice Batch multiple calls together (calls or delegatecalls)\n    /// @param targets Array of targets to call\n    /// @param data Array of data to pass with the calls\n    function multicall(\n        address[] calldata targets,\n        bytes[] calldata data\n    ) external payable {\n        if (targets.length != data.length) {\n            revert MulticallParamsMismatchError();\n        }\n\n        if (!registry.isWhitelistedCaller(msg.sender)) {\n            revert CallerNotWhitelisted(msg.sender);\n        }\n\n        for (uint256 i = 0; i != data.length;) {\n            if (targets[i] == address(0)) {\n                unchecked {\n                    ++i;\n                }\n                continue; // No-op\n            }\n\n            if (targets[i] != address(this)) {\n                if (!registry.isWhitelistedTarget(targets[i])) {\n                    revert TargetNotWhitelisted(targets[i]);\n                }\n            }\n\n            (bool success, bytes memory result) =\n                targets[i].delegatecall(data[i]);\n\n            if (!success) {\n                if (result.length == 0) revert();\n                assembly {\n                    revert(add(32, result), mload(result))\n                }\n            }\n            unchecked {\n                ++i;\n            }\n        }\n    }\n}\n"
      },
      "contracts/base/Admin.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\n/// @title Admin contract\n/// @author vfat.tools\n/// @notice Provides an administration mechanism allowing restricted functions\nabstract contract Admin {\n    /// ERRORS ///\n\n    /// @notice Thrown when the caller is not the admin\n    error NotAdminError(); //0xb5c42b3b\n\n    /// EVENTS ///\n\n    /// @notice Emitted when a new admin is set\n    /// @param oldAdmin Address of the old admin\n    /// @param newAdmin Address of the new admin\n    event AdminSet(address oldAdmin, address newAdmin);\n\n    /// STORAGE ///\n\n    /// @notice Address of the current admin\n    address public admin;\n\n    /// MODIFIERS ///\n\n    /// @dev Restricts a function to the admin\n    modifier onlyAdmin() {\n        if (msg.sender != admin) revert NotAdminError();\n        _;\n    }\n\n    /// WRITE FUNCTIONS ///\n\n    /// @param admin_ Address of the admin\n    constructor(address admin_) {\n        emit AdminSet(admin, admin_);\n        admin = admin_;\n    }\n\n    /// @notice Sets a new admin\n    /// @param newAdmin Address of the new admin\n    /// @custom:access Restricted to protocol admin.\n    function setAdmin(address newAdmin) external onlyAdmin {\n        emit AdminSet(admin, newAdmin);\n        admin = newAdmin;\n    }\n}\n"
      },
      "contracts/base/TimelockAdmin.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\n/// @title TimelockAdmin contract\n/// @author vfat.tools\n/// @notice Provides an timelockAdministration mechanism allowing restricted\n/// functions\nabstract contract TimelockAdmin {\n    /// ERRORS ///\n\n    /// @notice Thrown when the caller is not the timelockAdmin\n    error NotTimelockAdminError();\n\n    /// EVENTS ///\n\n    /// @notice Emitted when a new timelockAdmin is set\n    /// @param oldTimelockAdmin Address of the old timelockAdmin\n    /// @param newTimelockAdmin Address of the new timelockAdmin\n    event TimelockAdminSet(address oldTimelockAdmin, address newTimelockAdmin);\n\n    /// STORAGE ///\n\n    /// @notice Address of the current timelockAdmin\n    address public timelockAdmin;\n\n    /// MODIFIERS ///\n\n    /// @dev Restricts a function to the timelockAdmin\n    modifier onlyTimelockAdmin() {\n        if (msg.sender != timelockAdmin) revert NotTimelockAdminError();\n        _;\n    }\n\n    /// WRITE FUNCTIONS ///\n\n    /// @param timelockAdmin_ Address of the timelockAdmin\n    constructor(address timelockAdmin_) {\n        emit TimelockAdminSet(timelockAdmin, timelockAdmin_);\n        timelockAdmin = timelockAdmin_;\n    }\n\n    /// @notice Sets a new timelockAdmin\n    /// @dev Can only be called by the current timelockAdmin\n    /// @param newTimelockAdmin Address of the new timelockAdmin\n    function setTimelockAdmin(address newTimelockAdmin)\n        external\n        onlyTimelockAdmin\n    {\n        emit TimelockAdminSet(timelockAdmin, newTimelockAdmin);\n        timelockAdmin = newTimelockAdmin;\n    }\n}\n"
      },
      "contracts/strategies/modules/AccessControlModule.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"../../Sickle.sol\";\nimport \"../../SickleFactory.sol\";\n\ncontract AccessControlModule {\n    SickleFactory public immutable factory;\n\n    error NotOwner(address sender); // 30cd7471\n    error NotOwnerOrInternal(); // 25fbbab5\n    error NotOwnerOrApproved();\n    error NotOwnerOrApprovedOrInternal();\n    error SickleNotDeployed();\n    error NotRegisteredSickle();\n\n    constructor(SickleFactory factory_) {\n        factory = factory_;\n    }\n\n    modifier onlyRegisteredSickle() {\n        if (factory.admins(address(this)) == address(0)) {\n            revert NotRegisteredSickle();\n        }\n\n        _;\n    }\n\n    // @dev allow access only to the sickle's owner\n    // to use for all functions unless part of specific cases listed below\n    modifier checkOwner(address sickleAddress) {\n        // Calling the factory instead of the Sickle contract gives us the\n        // guarantee that the Sickle contract is genuine\n        if (msg.sender != factory.admins(sickleAddress)) {\n            revert NotOwner(msg.sender);\n        }\n\n        _;\n    }\n\n    // @dev allow access only to the sickle's owner or addresses approved by him\n    // to use only for functions such as claiming rewards or compounding rewards\n    modifier checkOwnerOrApproved(address sickleAddress) {\n        Sickle sickle = Sickle(payable(sickleAddress));\n\n        // Here we check if the Sickle  was really deployed, this gives use the\n        // guarantee that the contract that we are going to call is genuine\n        if (factory.admins(sickleAddress) == address(0)) {\n            revert SickleNotDeployed();\n        }\n\n        if (!sickle.isOwnerOrApproved(msg.sender)) revert NotOwnerOrApproved();\n\n        _;\n    }\n\n    // @dev allow access only to the sickle's owner or internal calls\n    // to use only for functions such as components of a complex action\n    modifier checkOwnerOrInternal(address sickleAddress) {\n        Sickle sickle = Sickle(payable(sickleAddress));\n\n        // Here we check if the Sickle  was really deployed, this gives use the\n        // guarantee that the contract that we are going to call is genuine\n        if (factory.admins(sickleAddress) == address(0)) {\n            revert SickleNotDeployed();\n        }\n\n        if (\n            msg.sender != factory.admins(sickleAddress)\n                && msg.sender != sickleAddress\n        ) revert NotOwnerOrInternal();\n\n        _;\n    }\n\n    // @dev allow access only to the sickle's owner or addresses approved by him\n    // or internal calls\n    // to use only for functions such as claiming rewards or components of a\n    // complex action\n    modifier checkOwnerOrApprovedOrInternal(address sickleAddress) {\n        Sickle sickle = Sickle(payable(sickleAddress));\n\n        // Here we check if the Sickle  was really deployed, this gives use the\n        // guarantee that the contract that we are going to call is genuine\n        if (factory.admins(sickleAddress) == address(0)) {\n            revert SickleNotDeployed();\n        }\n\n        if (\n            !sickle.isOwnerOrApproved(msg.sender) && msg.sender != sickleAddress\n        ) revert NotOwnerOrApprovedOrInternal();\n\n        _;\n    }\n}\n"
      },
      "contracts/strategies/modules/FeesModule.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"../../libraries/FeesLib.sol\";\nimport { IWETH9 } from \"../../interfaces/external/IWETH.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./DelegateModule.sol\";\n\naddress constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;\n\ncontract FeesModule is DelegateModule {\n    FeesLib public immutable feesLib;\n    address public immutable wrappedNativeAddress;\n\n    constructor(FeesLib feesLib_, address wrappedNativeAddress_) {\n        feesLib = feesLib_;\n        wrappedNativeAddress = wrappedNativeAddress_;\n    }\n\n    /// INTERNALS ///\n\n    function _charge_fees(\n        bytes32 feeHash,\n        address tokenToCharge,\n        uint256 baseAmount\n    ) internal returns (uint256 remainingAmount) {\n        (remainingAmount) = abi.decode(\n            _delegateTo(\n                address(feesLib),\n                abi.encodeCall(\n                    FeesLib.chargeFees, (feeHash, tokenToCharge, baseAmount)\n                )\n            ),\n            (uint256)\n        );\n    }\n\n    function _sickle_charge_fee(\n        address strategy,\n        bytes4 feeDescriptor,\n        address feeToken\n    ) public {\n        IWETH9 weth = IWETH9(wrappedNativeAddress);\n        uint256 feeBasis;\n        if (feeToken == ETH) {\n            weth.withdraw(weth.balanceOf(address(this)));\n            feeBasis = address(this).balance;\n        } else {\n            feeBasis = IERC20(feeToken).balanceOf(address(this));\n        }\n        _charge_fees(\n            keccak256(abi.encodePacked(strategy, feeDescriptor)),\n            feeToken,\n            feeBasis\n        );\n    }\n\n    function _sickle_charge_fees(\n        address strategy,\n        bytes4 feeDescriptor,\n        address[] memory feeTokens\n    ) external {\n        for (uint256 i = 0; i < feeTokens.length;) {\n            _sickle_charge_fee(strategy, feeDescriptor, feeTokens[i]);\n            unchecked {\n                i++;\n            }\n        }\n    }\n\n    function _sickle_charge_transaction_cost(\n        address recipient,\n        address wrappedNative,\n        uint256 amountToCharge\n    ) external {\n        _delegateTo(\n            address(feesLib),\n            abi.encodeCall(\n                FeesLib.chargeTransactionCost,\n                (recipient, wrappedNative, amountToCharge)\n            )\n        );\n    }\n}\n"
      },
      "contracts/strategies/modules/MsgValueModule.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\ncontract MsgValueModule {\n    error IncorrectMsgValue();\n\n    function _checkMsgValue(uint256 inputAmount, bool isNative) internal {\n        if (\n            // Input is native token but user sent incorrect amount\n            (isNative && inputAmount != msg.value)\n            // Input is ERC20 but user sent native token as well\n            || (!isNative && msg.value > 0)\n        ) {\n            revert IncorrectMsgValue();\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.2;\n\nimport \"../../utils/Address.sol\";\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Indicates that the contract has been initialized.\n     * @custom:oz-retyped-from bool\n     */\n    uint8 private _initialized;\n\n    /**\n     * @dev Indicates that the contract is in the process of being initialized.\n     */\n    bool private _initializing;\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint8 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n     * constructor.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        bool isTopLevelCall = !_initializing;\n        require(\n            (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),\n            \"Initializable: contract is already initialized\"\n        );\n        _initialized = 1;\n        if (isTopLevelCall) {\n            _initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            _initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: setting the version to 255 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint8 version) {\n        require(!_initializing && _initialized < version, \"Initializable: contract is already initialized\");\n        _initialized = version;\n        _initializing = true;\n        _;\n        _initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        require(_initializing, \"Initializable: contract is not initializing\");\n        _;\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        require(!_initializing, \"Initializable: contract is initializing\");\n        if (_initialized < type(uint8).max) {\n            _initialized = type(uint8).max;\n            emit Initialized(type(uint8).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint8) {\n        return _initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _initializing;\n    }\n}\n"
      },
      "contracts/SickleRegistry.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"./base/Admin.sol\";\n\nlibrary SickleRegistryEvents {\n    event CollectorChanged(address newCollector);\n    event FeesUpdated(bytes32[] feeHashes, uint256[] feesInBP);\n    event ReferralCodeCreated(bytes32 indexed code, address indexed referrer);\n\n    // Multicall caller and target whitelist status changes\n    event CallerStatusChanged(address caller, bool isWhitelisted);\n    event TargetStatusChanged(address target, bool isWhitelisted);\n}\n\n/// @title SickleRegistry contract\n/// @author vfat.tools\n/// @notice Manages the whitelisted contracts and the collector address\ncontract SickleRegistry is Admin {\n    /// ERRORS ///\n\n    error ArrayLengthMismatch(); // 0xa24a13a6\n    error FeeAboveMaxLimit(); // 0xd6cf7b5e\n    error InvalidReferralCode(); // 0xe55b4629\n\n    /// STORAGE ///\n\n    /// @notice Address of the fee collector\n    address public collector;\n\n    /// @notice Tracks the contracts that can be called through Sickle multicall\n    /// @return True if the contract is a whitelisted target\n    mapping(address => bool) public isWhitelistedTarget;\n\n    /// @notice Tracks the contracts that can call Sickle multicall\n    /// @return True if the contract is a whitelisted caller\n    mapping(address => bool) public isWhitelistedCaller;\n\n    /// @notice Keeps track of the referrers and their associated code\n    mapping(bytes32 => address) public referralCodes;\n\n    /// @notice Mapping for fee hashes (hash of the strategy contract addresses\n    /// and the function selectors) and their associated fees\n    /// @return The fee in basis points to apply to the transaction amount\n    mapping(bytes32 => uint256) public feeRegistry;\n\n    /// WRITE FUNCTIONS ///\n\n    /// @param admin_ Address of the admin\n    /// @param collector_ Address of the collector\n    constructor(address admin_, address collector_) Admin(admin_) {\n        collector = collector_;\n    }\n\n    /// @notice Updates the whitelist status for multiple multicall targets\n    /// @param targets Addresses of the contracts to update\n    /// @param isApproved New status for the contracts\n    /// @custom:access Restricted to protocol admin.\n    function setWhitelistedTargets(\n        address[] calldata targets,\n        bool isApproved\n    ) external onlyAdmin {\n        for (uint256 i; i < targets.length;) {\n            isWhitelistedTarget[targets[i]] = isApproved;\n            emit SickleRegistryEvents.TargetStatusChanged(\n                targets[i], isApproved\n            );\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /// @notice Updates the fee collector address\n    /// @param newCollector Address of the new fee collector\n    /// @custom:access Restricted to protocol admin.\n    function updateCollector(address newCollector) external onlyAdmin {\n        collector = newCollector;\n        emit SickleRegistryEvents.CollectorChanged(newCollector);\n    }\n\n    /// @notice Update the whitelist status for multiple multicall callers\n    /// @param callers Addresses of the callers\n    /// @param isApproved New status for the caller\n    /// @custom:access Restricted to protocol admin.\n    function setWhitelistedCallers(\n        address[] calldata callers,\n        bool isApproved\n    ) external onlyAdmin {\n        for (uint256 i; i < callers.length;) {\n            isWhitelistedCaller[callers[i]] = isApproved;\n            emit SickleRegistryEvents.CallerStatusChanged(\n                callers[i], isApproved\n            );\n\n            unchecked {\n                ++i;\n            }\n        }\n    }\n\n    /// @notice Associates a referral code to the address of the caller\n    function setReferralCode(bytes32 referralCode) external {\n        if (referralCodes[referralCode] != address(0)) {\n            revert InvalidReferralCode();\n        }\n\n        referralCodes[referralCode] = msg.sender;\n        emit SickleRegistryEvents.ReferralCodeCreated(referralCode, msg.sender);\n    }\n\n    /// @notice Update the fees for multiple strategy functions\n    /// @param feeHashes Array of fee hashes\n    /// @param feesArray Array of fees to apply (in basis points)\n    /// @custom:access Restricted to protocol admin.\n    function setFees(\n        bytes32[] calldata feeHashes,\n        uint256[] calldata feesArray\n    ) external onlyAdmin {\n        if (feeHashes.length != feesArray.length) {\n            revert ArrayLengthMismatch();\n        }\n\n        for (uint256 i = 0; i < feeHashes.length;) {\n            if (feesArray[i] <= 500) {\n                // maximum fee of 5%\n                feeRegistry[feeHashes[i]] = feesArray[i];\n            } else {\n                revert FeeAboveMaxLimit();\n            }\n            unchecked {\n                ++i;\n            }\n        }\n\n        emit SickleRegistryEvents.FeesUpdated(feeHashes, feesArray);\n    }\n}\n"
      },
      "contracts/SickleFactory.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/proxy/Clones.sol\";\n\nimport \"./Sickle.sol\";\nimport \"./base/Admin.sol\";\n\n/// @title SickleFactory contract\n/// @author vfat.tools\n/// @notice Factory deploying new Sickle contracts\ncontract SickleFactory is Admin {\n    /// EVENTS ///\n\n    /// @notice Emitted when a new Sickle contract is deployed\n    /// @param admin Address receiving the admin rights of the Sickle contract\n    /// @param sickle Address of the newly deployed Sickle contract\n    event Deploy(address indexed admin, address sickle);\n\n    /// @notice Thrown when the caller is not whitelisted\n    /// @param caller Address of the non-whitelisted caller\n    error CallerNotWhitelisted(address caller); // 0x252c8273\n\n    /// @notice Thrown when the factory is not active and a deploy is attempted\n    error NotActive(); // 0x80cb55e2\n\n    /// @notice Thrown when a Sickle contract is already deployed for a user\n    error SickleAlreadyDeployed(); //0xf6782ef1\n\n    /// STORAGE ///\n\n    mapping(address => address) private _sickles;\n    mapping(address => address) private _admins;\n    mapping(address => bytes32) public _referralCodes;\n\n    /// @notice Address of the SickleRegistry contract\n    SickleRegistry public immutable registry;\n\n    /// @notice Address of the Sickle implementation contract\n    address public immutable implementation;\n\n    /// @notice Address of the previous SickleFactory contract (if applicable)\n    SickleFactory public immutable previousFactory;\n\n    /// @notice Whether the factory is active (can deploy new Sickle contracts)\n    bool public isActive = true;\n\n    /// WRITE FUNCTIONS ///\n\n    /// @param admin_ Address of the admin\n    /// @param sickleRegistry_ Address of the SickleRegistry contract\n    /// @param sickleImplementation_ Address of the Sickle implementation\n    /// contract\n    /// @param previousFactory_ Address of the previous SickleFactory contract\n    /// if applicable\n    constructor(\n        address admin_,\n        address sickleRegistry_,\n        address sickleImplementation_,\n        address previousFactory_\n    ) Admin(admin_) {\n        registry = SickleRegistry(sickleRegistry_);\n        implementation = sickleImplementation_;\n        previousFactory = SickleFactory(previousFactory_);\n    }\n\n    /// @notice Update the isActive flag.\n    /// @dev Effectively pauses and unpauses new Sickle deployments.\n    /// @custom:access Restricted to protocol admin.\n    function setActive(bool active) external onlyAdmin {\n        isActive = active;\n    }\n\n    function _deploy(\n        address admin,\n        address approved,\n        bytes32 referralCode\n    ) internal returns (address sickle) {\n        sickle = Clones.cloneDeterministic(\n            implementation, keccak256(abi.encode(admin))\n        );\n        Sickle(payable(sickle)).initialize(admin, approved);\n        _sickles[admin] = sickle;\n        _admins[sickle] = admin;\n        if (referralCode != bytes32(0)) {\n            _referralCodes[sickle] = referralCode;\n        }\n        emit Deploy(admin, sickle);\n    }\n\n    function _getSickle(address admin) internal returns (address sickle) {\n        sickle = _sickles[admin];\n        if (sickle != address(0)) {\n            return sickle;\n        }\n        if (address(previousFactory) != address(0)) {\n            sickle = previousFactory.sickles(admin);\n            if (sickle != address(0)) {\n                _sickles[admin] = sickle;\n                _admins[sickle] = admin;\n                _referralCodes[sickle] = previousFactory.referralCodes(sickle);\n                return sickle;\n            }\n        }\n    }\n\n    /// @notice Predict the address of a Sickle contract for a specific user\n    /// @param admin Address receiving the admin rights of the Sickle contract\n    /// @return sickle Address of the predicted Sickle contract\n    function predict(address admin) external view returns (address) {\n        bytes32 salt = keccak256(abi.encode(admin));\n        return Clones.predictDeterministicAddress(implementation, salt);\n    }\n\n    /// @notice Returns the Sickle contract for a specific user\n    /// @param admin Address that owns the Sickle contract\n    /// @return sickle Address of the Sickle contract\n    function sickles(address admin) external view returns (address sickle) {\n        sickle = _sickles[admin];\n        if (sickle == address(0) && address(previousFactory) != address(0)) {\n            sickle = previousFactory.sickles(admin);\n        }\n    }\n\n    /// @notice Returns the admin for a specific Sickle contract\n    /// @param sickle Address of the Sickle contract\n    /// @return admin Address that owns the Sickle contract\n    function admins(address sickle) external view returns (address admin) {\n        admin = _admins[sickle];\n        if (admin == address(0) && address(previousFactory) != address(0)) {\n            admin = previousFactory.admins(sickle);\n        }\n    }\n\n    /// @notice Returns the referral code for a specific Sickle contract\n    /// @param sickle Address of the Sickle contract\n    /// @return referralCode Referral code for the user\n    function referralCodes(address sickle)\n        external\n        view\n        returns (bytes32 referralCode)\n    {\n        referralCode = _referralCodes[sickle];\n        if (\n            referralCode == bytes32(0) && address(previousFactory) != address(0)\n        ) {\n            referralCode = previousFactory.referralCodes(sickle);\n        }\n    }\n\n    /// @notice Deploys a new Sickle contract for a specific user, or returns\n    /// the existing one if it exists\n    /// @param admin Address receiving the admin rights of the Sickle contract\n    /// @param referralCode Referral code for the user\n    /// @return sickle Address of the deployed Sickle contract\n    function getOrDeploy(\n        address admin,\n        address approved,\n        bytes32 referralCode\n    ) external returns (address sickle) {\n        if (!isActive) {\n            revert NotActive();\n        }\n        if (!registry.isWhitelistedCaller(msg.sender)) {\n            revert CallerNotWhitelisted(msg.sender);\n        }\n        if ((sickle = _getSickle(admin)) != address(0)) {\n            return sickle;\n        }\n        return _deploy(admin, approved, referralCode);\n    }\n\n    /// @notice Deploys a new Sickle contract for a specific user\n    /// @dev Sickle contracts are deployed with create2, the address of the\n    /// admin is used as a salt, so all the Sickle addresses can be pre-computed\n    /// and only 1 Sickle will exist per address\n    /// @param referralCode Referral code for the user\n    /// @return sickle Address of the deployed Sickle contract\n    function deploy(\n        address approved,\n        bytes32 referralCode\n    ) external returns (address sickle) {\n        if (!isActive) {\n            revert NotActive();\n        }\n        if (_getSickle(msg.sender) != address(0)) {\n            revert SickleAlreadyDeployed();\n        }\n        return _deploy(msg.sender, approved, referralCode);\n    }\n}\n"
      },
      "contracts/libraries/FeesLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport { SafeTransferLib } from \"solmate/utils/SafeTransferLib.sol\";\nimport { WETH } from \"solmate/tokens/WETH.sol\";\n\nimport \"../SickleRegistry.sol\";\n\ncontract FeesLib {\n    event FeeCharged(bytes32 feesHash, uint256 amount, address token);\n    event TransactionCostCharged(address recipient, uint256 amount);\n\n    /// @notice Fees library version\n    uint256 public constant VERSION = 1;\n\n    /// @notice Sickle registry address\n    SickleRegistry public immutable registry;\n\n    constructor(SickleRegistry registry_) {\n        registry = registry_;\n    }\n\n    /**\n     * @notice Strategy contract charges fee to user depending on the type of\n     * action and sends funds to the collector address\n     * @param feeHash Fee hash (address of the strategy and function selector)\n     * @param tokenToCharge Address of the token from which an amount will be\n     * charged (zero address if native token)\n     * @param baseAmount Amount of the transaction serving as a base for fee\n     * calculation\n     */\n    function chargeFees(\n        bytes32 feeHash,\n        address tokenToCharge,\n        uint256 baseAmount\n    ) public payable returns (uint256) {\n        uint256 fee = registry.feeRegistry(feeHash);\n\n        if (fee == 0) {\n            return baseAmount;\n        }\n\n        uint256 amountToCharge = baseAmount * fee / 10_000;\n\n        if (\n            tokenToCharge == address(0)\n                || tokenToCharge == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\n        ) {\n            SafeTransferLib.safeTransferETH(\n                registry.collector(), amountToCharge\n            );\n        } else {\n            SafeTransferLib.safeTransfer(\n                tokenToCharge, registry.collector(), amountToCharge\n            );\n        }\n\n        emit FeeCharged(feeHash, amountToCharge, tokenToCharge);\n        return baseAmount - amountToCharge;\n    }\n\n    function chargeTransactionCost(\n        address recipient,\n        address wrappedNative,\n        uint256 amountToCharge\n    ) public payable {\n        WETH(payable(wrappedNative)).withdraw(amountToCharge);\n        SafeTransferLib.safeTransferETH(recipient, amountToCharge);\n        emit TransactionCostCharged(recipient, amountToCharge);\n    }\n}\n"
      },
      "contracts/interfaces/external/IWETH.sol": {
        "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity >=0.8.9;\n\ninterface IWETH9 {\n    function deposit() external payable;\n\n    function withdraw(uint256 wad) external;\n\n    function approve(address guy, uint256 wad) external returns (bool);\n\n    function balanceOf(address account) external view returns (uint256);\n\n    function allowance(\n        address account,\n        address spender\n    ) external view returns (uint256);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"
      },
      "contracts/strategies/modules/DelegateModule.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\ncontract DelegateModule {\n    function _delegateTo(\n        address to,\n        bytes memory data\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory result) = to.delegatecall(data);\n\n        if (!success) {\n            if (result.length == 0) revert();\n            assembly {\n                revert(add(32, result), mload(result))\n            }\n        }\n\n        return result;\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     *\n     * [IMPORTANT]\n     * ====\n     * You shouldn't rely on `isContract` to protect against flash loan attacks!\n     *\n     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n     * constructor.\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize/address.code.length, which returns 0\n        // for contracts in construction, since the code is only stored at the end\n        // of the constructor execution.\n\n        return account.code.length > 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value\n    ) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(\n        address target,\n        bytes memory data,\n        uint256 value,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        require(address(this).balance >= value, \"Address: insufficient balance for call\");\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(\n        address target,\n        bytes memory data,\n        string memory errorMessage\n    ) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n     *\n     * _Available since v4.8._\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal view returns (bytes memory) {\n        if (success) {\n            if (returndata.length == 0) {\n                // only check isContract if the call was successful and the return data is empty\n                // otherwise we already know that it was a contract\n                require(isContract(target), \"Address: call to non-contract\");\n            }\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n     * revert reason or using the provided one.\n     *\n     * _Available since v4.3._\n     */\n    function verifyCallResult(\n        bool success,\n        bytes memory returndata,\n        string memory errorMessage\n    ) internal pure returns (bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            _revert(returndata, errorMessage);\n        }\n    }\n\n    function _revert(bytes memory returndata, string memory errorMessage) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert(errorMessage);\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/proxy/Clones.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for\n * deploying minimal proxy contracts, also known as \"clones\".\n *\n * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies\n * > a minimal bytecode implementation that delegates all calls to a known, fixed address.\n *\n * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`\n * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the\n * deterministic method.\n *\n * _Available since v3.4._\n */\nlibrary Clones {\n    /**\n     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n     *\n     * This function uses the create opcode, which should never revert.\n     */\n    function clone(address implementation) internal returns (address instance) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n            // of the `implementation` address with the bytecode before the address.\n            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n            instance := create(0, 0x09, 0x37)\n        }\n        require(instance != address(0), \"ERC1167: create failed\");\n    }\n\n    /**\n     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n     *\n     * This function uses the create2 opcode and a `salt` to deterministically deploy\n     * the clone. Using the same `implementation` and `salt` multiple time will revert, since\n     * the clones cannot be deployed twice at the same address.\n     */\n    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n            // of the `implementation` address with the bytecode before the address.\n            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n            instance := create2(0, 0x09, 0x37, salt)\n        }\n        require(instance != address(0), \"ERC1167: create2 failed\");\n    }\n\n    /**\n     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n     */\n    function predictDeterministicAddress(\n        address implementation,\n        bytes32 salt,\n        address deployer\n    ) internal pure returns (address predicted) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let ptr := mload(0x40)\n            mstore(add(ptr, 0x38), deployer)\n            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)\n            mstore(add(ptr, 0x14), implementation)\n            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)\n            mstore(add(ptr, 0x58), salt)\n            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))\n            predicted := keccak256(add(ptr, 0x43), 0x55)\n        }\n    }\n\n    /**\n     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n     */\n    function predictDeterministicAddress(address implementation, bytes32 salt)\n        internal\n        view\n        returns (address predicted)\n    {\n        return predictDeterministicAddress(implementation, salt, address(this));\n    }\n}\n"
      },
      "lib/solmate/src/utils/SafeTransferLib.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"../tokens/ERC20.sol\";\n\n/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)\n/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.\n/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.\nlibrary SafeTransferLib {\n    /*//////////////////////////////////////////////////////////////\n                                ERRORS\n    //////////////////////////////////////////////////////////////*/\n\n    error ETHTransferFailed();\n    error TransferFromFailed();\n    error TransferFailed();\n    error ApproveFailed();\n\n    /*//////////////////////////////////////////////////////////////\n                             ETH OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function safeTransferETH(address to, uint256 amount) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Transfer the ETH and store if it succeeded or not.\n            success := call(gas(), to, amount, 0, 0, 0, 0)\n        }\n\n        if (!success) revert ETHTransferFailed();\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                            ERC20 OPERATIONS\n    //////////////////////////////////////////////////////////////*/\n\n    function safeTransferFrom(\n        address token,\n        address from,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Get a pointer to some free memory.\n            let freeMemoryPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n            mstore(add(freeMemoryPointer, 4), from) // Append the \"from\" argument.\n            mstore(add(freeMemoryPointer, 36), to) // Append the \"to\" argument.\n            mstore(add(freeMemoryPointer, 68), amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.\n                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n                // Counterintuitively, this call must be positioned second to the or() call in the\n                // surrounding and() call or else returndatasize() will be zero during the computation.\n                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)\n            )\n        }\n\n        if (!success) revert TransferFromFailed();\n    }\n\n    function safeTransfer(\n        address token,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Get a pointer to some free memory.\n            let freeMemoryPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n            mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n            mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n                // Counterintuitively, this call must be positioned second to the or() call in the\n                // surrounding and() call or else returndatasize() will be zero during the computation.\n                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n            )\n        }\n\n        if (!success) revert TransferFailed();\n    }\n\n    function safeApprove(\n        address token,\n        address to,\n        uint256 amount\n    ) internal {\n        bool success;\n\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Get a pointer to some free memory.\n            let freeMemoryPointer := mload(0x40)\n\n            // Write the abi-encoded calldata into memory, beginning with the function selector.\n            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)\n            mstore(add(freeMemoryPointer, 4), to) // Append the \"to\" argument.\n            mstore(add(freeMemoryPointer, 36), amount) // Append the \"amount\" argument.\n\n            success := and(\n                // Set success to whether the call reverted, if not we check it either\n                // returned exactly 1 (can't just be non-zero data), or had no return data.\n                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),\n                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.\n                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.\n                // Counterintuitively, this call must be positioned second to the or() call in the\n                // surrounding and() call or else returndatasize() will be zero during the computation.\n                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)\n            )\n        }\n\n        if (!success) revert ApproveFailed();\n    }\n}\n"
      },
      "lib/solmate/src/tokens/WETH.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\nimport {ERC20} from \"./ERC20.sol\";\n\nimport {SafeTransferLib} from \"../utils/SafeTransferLib.sol\";\n\n/// @notice Minimalist and modern Wrapped Ether implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol)\n/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)\ncontract WETH is ERC20(\"Wrapped Ether\", \"WETH\", 18) {\n    using SafeTransferLib for address;\n\n    event Deposit(address indexed from, uint256 amount);\n\n    event Withdrawal(address indexed to, uint256 amount);\n\n    function deposit() public payable virtual {\n        _mint(msg.sender, msg.value);\n\n        emit Deposit(msg.sender, msg.value);\n    }\n\n    function withdraw(uint256 amount) public virtual {\n        _burn(msg.sender, amount);\n\n        emit Withdrawal(msg.sender, amount);\n\n        msg.sender.safeTransferETH(amount);\n    }\n\n    receive() external payable virtual {\n        deposit();\n    }\n}\n"
      },
      "lib/solmate/src/tokens/ERC20.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n\n    event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n    /*//////////////////////////////////////////////////////////////\n                            METADATA STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    string public name;\n\n    string public symbol;\n\n    uint8 public immutable decimals;\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC20 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 public totalSupply;\n\n    mapping(address => uint256) public balanceOf;\n\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    /*//////////////////////////////////////////////////////////////\n                            EIP-2612 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal immutable INITIAL_CHAIN_ID;\n\n    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n    mapping(address => uint256) public nonces;\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals\n    ) {\n        name = _name;\n        symbol = _symbol;\n        decimals = _decimals;\n\n        INITIAL_CHAIN_ID = block.chainid;\n        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               ERC20 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function approve(address spender, uint256 amount) public virtual returns (bool) {\n        allowance[msg.sender][spender] = amount;\n\n        emit Approval(msg.sender, spender, amount);\n\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) public virtual returns (bool) {\n        balanceOf[msg.sender] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(msg.sender, to, amount);\n\n        return true;\n    }\n\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public virtual returns (bool) {\n        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n        balanceOf[from] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        return true;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             EIP-2612 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) public virtual {\n        require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n        // Unchecked because the only math done is incrementing\n        // the owner's nonce which cannot realistically overflow.\n        unchecked {\n            address recoveredAddress = ecrecover(\n                keccak256(\n                    abi.encodePacked(\n                        \"\\x19\\x01\",\n                        DOMAIN_SEPARATOR(),\n                        keccak256(\n                            abi.encode(\n                                keccak256(\n                                    \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n                                ),\n                                owner,\n                                spender,\n                                value,\n                                nonces[owner]++,\n                                deadline\n                            )\n                        )\n                    )\n                ),\n                v,\n                r,\n                s\n            );\n\n            require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n            allowance[recoveredAddress][spender] = value;\n        }\n\n        emit Approval(owner, spender, value);\n    }\n\n    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n    }\n\n    function computeDomainSeparator() internal view virtual returns (bytes32) {\n        return\n            keccak256(\n                abi.encode(\n                    keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                    keccak256(bytes(name)),\n                    keccak256(\"1\"),\n                    block.chainid,\n                    address(this)\n                )\n            );\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL MINT/BURN LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _mint(address to, uint256 amount) internal virtual {\n        totalSupply += amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(address(0), to, amount);\n    }\n\n    function _burn(address from, uint256 amount) internal virtual {\n        balanceOf[from] -= amount;\n\n        // Cannot underflow because a user's balance\n        // will never be larger than the total supply.\n        unchecked {\n            totalSupply -= amount;\n        }\n\n        emit Transfer(from, address(0), amount);\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "solmate/=lib/solmate/src/",
        "@openzeppelin/=lib/openzeppelin-contracts/",
        "@uniswap/v3-periphery/=lib/v3-periphery/",
        "@uniswap/v3-core/=lib/v3-core/",
        "ds-test/=lib/solmate/lib/ds-test/src/",
        "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
        "forge-std/=lib/forge-std/src/",
        "morpho-blue/=lib/morpho-blue/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "viaIR": false,
      "libraries": {}
    }
  }}