creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/libraries/FeesLib.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport { SafeTransferLib } from \"solmate/utils/SafeTransferLib.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { WETH } from \"solmate/tokens/WETH.sol\";\n\nimport \"../Sickle.sol\";\nimport \"../SickleRegistry.sol\";\n\nlibrary FeesLibEvents {\n    event FeeCharged(bytes32 feesHash, uint256 amount, address token);\n}\n\ncontract FeesLib {\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 FeesLibEvents.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    }\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/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"
      },
      "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"
      },
      "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/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    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    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    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    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"
      },
      "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"
      },
      "contracts/base/SickleStorage.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/proxy/utils/Initializable.sol\";\n\nimport \"../SickleRegistry.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    /// @dev Can only be called by the current admin\n    /// @param newAdmin Address of the new admin\n    function setAdmin(address newAdmin) external onlyAdmin {\n        emit AdminSet(admin, newAdmin);\n        admin = newAdmin;\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"
      },
      "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"
      }
    },
    "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/forge-std/lib/ds-test/src/",
        "forge-std/=lib/forge-std/src/",
        "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": {}
    }
  }}