generated from crispymangoes/forge-template
-
Notifications
You must be signed in to change notification settings - Fork 8
/
LimitOrderRegistryLensVerification.json
1 lines (1 loc) · 96.1 KB
/
LimitOrderRegistryLensVerification.json
1
{"language":"Solidity","sources":{"lib/chainlink/contracts/src/v0.8/interfaces/AggregatorInterface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorInterface {\n function latestAnswer() external view returns (int256);\n\n function latestTimestamp() external view returns (uint256);\n\n function latestRound() external view returns (uint256);\n\n function getAnswer(uint256 roundId) external view returns (int256);\n\n function getTimestamp(uint256 roundId) external view returns (uint256);\n\n event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);\n\n event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);\n}\n"},"lib/chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./AggregatorInterface.sol\";\nimport \"./AggregatorV3Interface.sol\";\n\ninterface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}\n"},"lib/chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AggregatorV3Interface {\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n function getRoundData(uint80 _roundId)\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n );\n}\n"},"lib/chainlink/contracts/src/v0.8/interfaces/AutomationCompatibleInterface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AutomationCompatibleInterface {\n /**\n * @notice method that is simulated by the keepers to see if any work actually\n * needs to be performed. This method does does not actually need to be\n * executable, and since it is only ever simulated it can consume lots of gas.\n * @dev To ensure that it is never called, you may want to add the\n * cannotExecute modifier from KeeperBase to your implementation of this\n * method.\n * @param checkData specified in the upkeep registration so it is always the\n * same for a registered upkeep. This can easily be broken down into specific\n * arguments using `abi.decode`, so multiple upkeeps can be registered on the\n * same contract and easily differentiated by the contract.\n * @return upkeepNeeded boolean to indicate whether the keeper should call\n * performUpkeep or not.\n * @return performData bytes that the keeper should call performUpkeep with, if\n * upkeep is needed. If you would like to encode data to decode later, try\n * `abi.encode`.\n */\n function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData);\n\n /**\n * @notice method that is actually executed by the keepers, via the registry.\n * The data returned by the checkUpkeep simulation will be passed into\n * this method to actually be executed.\n * @dev The input to this method should not be trusted, and the caller of the\n * method should not even be restricted to any single registry. Anyone should\n * be able call it, and the input should be validated, there is no guarantee\n * that the data passed in is the performData returned from checkUpkeep. This\n * could happen due to malicious keepers, racing keepers, or simply a state\n * change while the performUpkeep transaction is waiting for confirmation.\n * Always validate the data passed in.\n * @param performData is the data which was passed back from the checkData\n * simulation. If it is encoded, it can easily be decoded into other types by\n * calling `abi.decode`. This data should not be trusted, and should be\n * validated against the contract's current state.\n */\n function performUpkeep(bytes calldata performData) external;\n}\n"},"lib/chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface LinkTokenInterface {\n function allowance(address owner, address spender) external view returns (uint256 remaining);\n\n function approve(address spender, uint256 value) external returns (bool success);\n\n function balanceOf(address owner) external view returns (uint256 balance);\n\n function decimals() external view returns (uint8 decimalPlaces);\n\n function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);\n\n function increaseApproval(address spender, uint256 subtractedValue) external;\n\n function name() external view returns (string memory tokenName);\n\n function symbol() external view returns (string memory tokenSymbol);\n\n function totalSupply() external view returns (uint256 totalTokensIssued);\n\n function transfer(address to, uint256 value) external returns (bool success);\n\n function transferAndCall(\n address to,\n uint256 value,\n bytes calldata data\n ) external returns (bool success);\n\n function transferFrom(\n address from,\n address to,\n uint256 value\n ) external returns (bool success);\n}\n"},"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n"},"lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721Receiver.sol\";\n\n/**\n * @dev Implementation of the {IERC721Receiver} interface.\n *\n * Accepts all token transfers.\n * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.\n */\ncontract ERC721Holder is IERC721Receiver {\n /**\n * @dev See {IERC721Receiver-onERC721Received}.\n *\n * Always returns `IERC721Receiver.onERC721Received.selector`.\n */\n function onERC721Received(\n address,\n address,\n uint256,\n bytes memory\n ) public virtual override returns (bytes4) {\n return this.onERC721Received.selector;\n }\n}\n"},"lib/openzeppelin-contracts/contracts/utils/Context.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n"},"lib/solmate/src/auth/Owned.sol":{"content":"// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)\nabstract contract Owned {\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event OwnershipTransferred(address indexed user, address indexed newOwner);\n\n /*//////////////////////////////////////////////////////////////\n OWNERSHIP STORAGE\n //////////////////////////////////////////////////////////////*/\n\n address public owner;\n\n modifier onlyOwner() virtual {\n require(msg.sender == owner, \"UNAUTHORIZED\");\n\n _;\n }\n\n /*//////////////////////////////////////////////////////////////\n CONSTRUCTOR\n //////////////////////////////////////////////////////////////*/\n\n constructor(address _owner) {\n owner = _owner;\n\n emit OwnershipTransferred(address(0), _owner);\n }\n\n /*//////////////////////////////////////////////////////////////\n OWNERSHIP LOGIC\n //////////////////////////////////////////////////////////////*/\n\n function transferOwnership(address newOwner) public virtual onlyOwner {\n owner = newOwner;\n\n emit OwnershipTransferred(msg.sender, newOwner);\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"},"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 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 require(success, \"ETH_TRANSFER_FAILED\");\n }\n\n /*//////////////////////////////////////////////////////////////\n ERC20 OPERATIONS\n //////////////////////////////////////////////////////////////*/\n\n function safeTransferFrom(\n ERC20 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 require(success, \"TRANSFER_FROM_FAILED\");\n }\n\n function safeTransfer(\n ERC20 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 require(success, \"TRANSFER_FAILED\");\n }\n\n function safeApprove(\n ERC20 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 require(success, \"APPROVE_FAILED\");\n }\n}\n"},"src/LimitOrderRegistry.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.16;\n\nimport { ERC20 } from \"@solmate/tokens/ERC20.sol\";\nimport { SafeTransferLib } from \"@solmate/utils/SafeTransferLib.sol\";\nimport { AutomationCompatibleInterface } from \"@chainlink/contracts/src/v0.8/interfaces/AutomationCompatibleInterface.sol\";\nimport { Owned } from \"@solmate/auth/Owned.sol\";\nimport { UniswapV3Pool } from \"src/interfaces/uniswapV3/UniswapV3Pool.sol\";\nimport { NonfungiblePositionManager } from \"src/interfaces/uniswapV3/NonfungiblePositionManager.sol\";\nimport { ERC721Holder } from \"@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol\";\nimport { LinkTokenInterface } from \"@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol\";\nimport { IKeeperRegistrar, RegistrationParams } from \"src/interfaces/chainlink/IKeeperRegistrar.sol\";\nimport { Context } from \"@openzeppelin/contracts/utils/Context.sol\";\nimport { IChainlinkAggregator } from \"src/interfaces/chainlink/IChainlinkAggregator.sol\";\n\n/**\n * @title Limit Order Registry\n * @notice Allows users to create decentralized limit orders.\n * @dev DO NOT PLACE LIMIT ORDERS FOR STRONGLY CORRELATED ASSETS.\n * - If a stable coin pair were to temporarily depeg, and a user places a limit order\n * whose tick range encompasses the normal trading tick, there is NO way to cancel the order\n * because the order is mixed. The user would have to wait for another depeg event to happen\n * so that the order can be fulfilled, or the order can be cancelled.\n * @author crispymangoes\n */\ncontract LimitOrderRegistry is Owned, AutomationCompatibleInterface, ERC721Holder, Context {\n using SafeTransferLib for ERC20;\n\n /*//////////////////////////////////////////////////////////////\n STRUCTS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Stores linked list center values, and frequently used pool values.\n * @param centerHead Linked list center value closer to head of the list\n * @param centerTail Linked list center value closer to tail of the list\n * @param token0 ERC20 token0 of the pool\n * @param token1 ERC20 token1 of the pool\n * @param fee Uniswap V3 pool fee\n */\n struct PoolData {\n uint256 centerHead;\n uint256 centerTail;\n ERC20 token0;\n ERC20 token1;\n uint24 fee;\n }\n\n /**\n * @notice Stores information about batches of orders.\n * @dev User orders can be batched together if they share the same target price.\n * @param direction Determines what direction the tick must move in order for the order to be filled\n * - true, pool tick must INCREASE to fill this order\n * - false, pool tick must DECREASE to fill this order\n * @param tickUpper The upper tick of the underlying LP position\n * @param tickLower The lower tick of the underlying LP position\n * @param userCount The number of users in this batch order\n * @param batchId Unique id used to distinguish this batch order from another batch order in the past that used the same LP position\n * @param token0Amount The amount of token0 in this order\n * @param token1Amount The amount of token1 in this order\n * @param head The next node in the linked list when moving toward the head\n * @param tail The next node in the linked list when moving toward the tail\n */\n struct BatchOrder {\n bool direction;\n int24 tickUpper;\n int24 tickLower;\n uint64 userCount;\n uint128 batchId;\n uint128 token0Amount;\n uint128 token1Amount;\n uint256 head;\n uint256 tail;\n }\n\n /**\n * @notice Stores information needed for users to make claims.\n * @param pool The Uniswap V3 pool the batch order was in\n * @param token0Amount The amount of token0 in the order\n * @param token1Amount The amount of token1 in the order\n * @param feePerUser The native token fee that must be paid on order claiming\n * @param direction The underlying order direction, used to determine input/output token of the order\n * @param isReadyForClaim Explicit bool indicating whether or not this order is ready to be claimed\n */\n struct Claim {\n UniswapV3Pool pool;\n uint128 token0Amount; //Can either be the deposit amount or the amount got out of liquidity changing to the other token\n uint128 token1Amount;\n uint128 feePerUser; // Fee in terms of network native asset.\n bool direction; //Determines the token out\n bool isReadyForClaim;\n }\n\n /**\n * @notice Struct used to store variables needed during order creation.\n * @param tick The target tick of the order\n * @param upper The upper tick of the underlying LP position\n * @param lower The lower tick of the underlying LP position\n * @param userTotal The total amount of assets the user has in the order\n * @param positionId The underling LP position token id this order is adding liquidity to\n * @param amount0 Can be the amount of assets user added to the order, based off orders direction\n * @param amount1 Can be the amount of assets user added to the order, based off orders direction\n */\n struct OrderDetails {\n int24 tick;\n int24 upper;\n int24 lower;\n uint128 userTotal;\n uint256 positionId;\n uint128 amount0;\n uint128 amount1;\n }\n\n /*//////////////////////////////////////////////////////////////\n GLOBAL STATE\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Stores swap fees earned from limit order where the input token earns swap fees.\n */\n mapping(address => uint256) public tokenToSwapFees;\n\n /**\n * @notice Used to store claim information needed when users are claiming their orders.\n */\n mapping(uint128 => Claim) public claim;\n\n /**\n * @notice Stores the pools center head/tail, as well as frequently read values.\n */\n mapping(UniswapV3Pool => PoolData) public poolToData;\n\n /**\n * @notice Maps tick ranges to LP positions owned by this contract.\n */\n mapping(int24 => mapping(int24 => uint256)) public getPositionFromTicks; // maps lower -> upper -> positionId\n\n /**\n * @notice The minimum amount of assets required to create a `newOrder`.\n * @dev Changeable by owner.\n */\n mapping(ERC20 => uint256) public minimumAssets;\n\n /**\n * @notice Approximated amount of gas needed to fulfill 1 BatchOrder.\n * @dev Changeable by owner.\n */\n uint32 public upkeepGasLimit = 300_000;\n\n /**\n * @notice Approximated gas price used to fulfill orders.\n * @dev Changeable by owner.\n */\n uint32 public upkeepGasPrice = 30;\n\n /**\n * @notice Max number of orders that can be filled in 1 upkeep call.\n * @dev Changeable by owner.\n */\n uint16 public maxFillsPerUpkeep = 10;\n\n /**\n * @notice Value is incremented whenever a new BatchOrder is added to the `orderBook`.\n * @dev Zero is reserved.\n */\n uint128 public batchCount = 1;\n\n /**\n * @notice Mapping is used to store user deposit amounts in each BatchOrder.\n */\n mapping(uint128 => mapping(address => uint128)) private batchIdToUserDepositAmount;\n\n /**\n * @notice The `orderBook` maps Uniswap V3 token ids to BatchOrder information.\n * @dev Each BatchOrder contains a head and tail value which effectively,\n * which means BatchOrders are connected using a doubley linked list.\n */\n mapping(uint256 => BatchOrder) public orderBook;\n\n /**\n * @notice Chainlink Automation Registrar contract.\n */\n IKeeperRegistrar public registrar; // Mainnet 0xDb8e8e2ccb5C033938736aa89Fe4fa1eDfD15a1d\n\n /**\n * @notice Whether or not the contract is shutdown in case of an emergency.\n */\n bool public isShutdown;\n\n /**\n * @notice Chainlink Fast Gas Feed for ETH Mainnet.\n */\n address public fastGasFeed = 0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C;\n\n /**\n * @notice The max possible gas the owner can set for the gas limit.\n */\n uint32 public constant MAX_GAS_LIMIT = 500_000;\n\n /**\n * @notice The max possible gas price the owner can set for the gas price.\n * @dev In units of gwei.\n */\n uint32 public constant MAX_GAS_PRICE = 1_000;\n\n /**\n * @notice The max number of orders that can be fulfilled in a single upkeep TX.\n */\n uint16 public constant MAX_FILLS_PER_UPKEEP = 20;\n\n /*//////////////////////////////////////////////////////////////\n MODIFIERS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Prevent a function from being called during a shutdown.\n */\n modifier whenNotShutdown() {\n if (isShutdown) revert LimitOrderRegistry__ContractShutdown();\n\n _;\n }\n\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event NewOrder(address user, address pool, uint128 amount, uint128 userTotal, BatchOrder effectedOrder);\n event ClaimOrder(address user, uint128 batchId, uint256 amount);\n event CancelOrder(address user, uint128 amount0, uint128 amount1, BatchOrder effectedOrder);\n event OrderFilled(uint256 batchId, address pool);\n event ShutdownChanged(bool isShutdown);\n event LimitOrderSetup(address pool);\n\n /*//////////////////////////////////////////////////////////////\n ERRORS\n //////////////////////////////////////////////////////////////*/\n\n error LimitOrderRegistry__OrderITM(int24 currentTick, int24 targetTick, bool direction);\n error LimitOrderRegistry__PoolAlreadySetup(address pool);\n error LimitOrderRegistry__PoolNotSetup(address pool);\n error LimitOrderRegistry__InvalidTargetTick(int24 targetTick, int24 tickSpacing);\n error LimitOrderRegistry__UserNotFound(address user, uint256 batchId);\n error LimitOrderRegistry__InvalidPositionId();\n error LimitOrderRegistry__NoLiquidityInOrder();\n error LimitOrderRegistry__NoOrdersToFulfill();\n error LimitOrderRegistry__CenterITM();\n error LimitOrderRegistry__OrderNotInList(uint256 tokenId);\n error LimitOrderRegistry__MinimumNotSet(address asset);\n error LimitOrderRegistry__MinimumNotMet(address asset, uint256 minimum, uint256 amount);\n error LimitOrderRegistry__InvalidTickRange(int24 upper, int24 lower);\n error LimitOrderRegistry__ZeroFeesToWithdraw(address token);\n error LimitOrderRegistry__ZeroNativeBalance();\n error LimitOrderRegistry__InvalidBatchId();\n error LimitOrderRegistry__OrderNotReadyToClaim(uint128 batchId);\n error LimitOrderRegistry__ContractShutdown();\n error LimitOrderRegistry__ContractNotShutdown();\n error LimitOrderRegistry__InvalidGasLimit();\n error LimitOrderRegistry__InvalidGasPrice();\n error LimitOrderRegistry__InvalidFillsPerUpkeep();\n\n /*//////////////////////////////////////////////////////////////\n ENUMS\n //////////////////////////////////////////////////////////////*/\n\n enum OrderStatus {\n ITM,\n OTM,\n MIXED\n }\n\n /*//////////////////////////////////////////////////////////////\n IMMUTABLES\n //////////////////////////////////////////////////////////////*/\n\n ERC20 public immutable WRAPPED_NATIVE; // Mainnet 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\n NonfungiblePositionManager public immutable POSITION_MANAGER; // Mainnet 0xC36442b4a4522E871399CD717aBDD847Ab11FE88\n\n LinkTokenInterface public immutable LINK; // Mainnet 0x514910771AF9Ca656af840dff83E8264EcF986CA\n\n constructor(\n address _owner,\n NonfungiblePositionManager _positionManager,\n ERC20 wrappedNative,\n LinkTokenInterface link,\n IKeeperRegistrar _registrar,\n address _fastGasFeed\n ) Owned(_owner) {\n POSITION_MANAGER = _positionManager;\n WRAPPED_NATIVE = wrappedNative;\n LINK = link;\n registrar = _registrar;\n fastGasFeed = _fastGasFeed;\n }\n\n /*//////////////////////////////////////////////////////////////\n OWNER LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice No input validation is done because it is in the owners best interest to choose a valid registrar.\n */\n function setRegistrar(IKeeperRegistrar _registrar) external onlyOwner {\n registrar = _registrar;\n }\n\n /**\n * @notice Allows owner to set the fills per upkeep.\n */\n function setMaxFillsPerUpkeep(uint16 newVal) external onlyOwner {\n if (newVal == 0 || newVal > MAX_FILLS_PER_UPKEEP) revert LimitOrderRegistry__InvalidFillsPerUpkeep();\n maxFillsPerUpkeep = newVal;\n }\n\n /**\n * @notice Allows owner to setup a new limit order for a new pool.\n * @dev New Limit orders, should have a keeper to fulfill orders.\n * @dev If `initialUpkeepFunds` is zero, upkeep creation is skipped.\n */\n function setupLimitOrder(UniswapV3Pool pool, uint256 initialUpkeepFunds) external onlyOwner {\n // Check if Limit Order is already setup for `pool`.\n if (address(poolToData[pool].token0) != address(0)) revert LimitOrderRegistry__PoolAlreadySetup(address(pool));\n\n // Create Upkeep.\n if (initialUpkeepFunds > 0) {\n // Owner wants to automatically create an upkeep for new pool.\n // SafeTransferLib.safeTransferFrom(ERC20(address(LINK)), owner, address(this), initialUpkeepFunds);\n ERC20(address(LINK)).safeTransferFrom(owner, address(this), initialUpkeepFunds);\n ERC20(address(LINK)).safeApprove(address(registrar), initialUpkeepFunds);\n RegistrationParams memory params = RegistrationParams({\n name: \"Limit Order Registry\",\n encryptedEmail: abi.encode(0),\n upkeepContract: address(this),\n gasLimit: uint32(maxFillsPerUpkeep * upkeepGasLimit),\n adminAddress: owner,\n checkData: abi.encode(pool),\n offchainConfig: abi.encode(0),\n amount: uint96(initialUpkeepFunds)\n });\n registrar.registerUpkeep(params);\n }\n\n // poolToData\n poolToData[pool] = PoolData({\n centerHead: 0,\n centerTail: 0,\n token0: ERC20(pool.token0()),\n token1: ERC20(pool.token1()),\n fee: pool.fee()\n });\n\n emit LimitOrderSetup(address(pool));\n }\n\n /**\n * @notice Allows owner to set the minimum assets used to create `newOrder`s.\n * @dev This value can be zero, but then this contract can be griefed by an attacker spamming low liquidity orders.\n */\n function setMinimumAssets(uint256 amount, ERC20 asset) external onlyOwner {\n minimumAssets[asset] = amount;\n }\n\n /**\n * @notice Allows owner to change the gas limit value used to determine the Native asset fee needed to claim orders.\n * @dev premium should be factored into this value.\n */\n function setUpkeepGasLimit(uint32 gasLimit) external onlyOwner {\n if (gasLimit > MAX_GAS_LIMIT) revert LimitOrderRegistry__InvalidGasLimit();\n upkeepGasLimit = gasLimit;\n }\n\n /**\n * @notice Allows owner to change the gas price used to determine the Native asset fee needed to claim orders.\n * @dev `gasPrice` uses units of gwei.\n */\n function setUpkeepGasPrice(uint32 gasPrice) external onlyOwner {\n if (gasPrice > MAX_GAS_PRICE) revert LimitOrderRegistry__InvalidGasPrice();\n upkeepGasPrice = gasPrice;\n }\n\n /**\n * @notice Allows owner to set the fast gas feed.\n */\n function setFastGasFeed(address feed) external onlyOwner {\n fastGasFeed = feed;\n }\n\n /**\n * @notice Allows owner to withdraw swap fees earned from the input token of orders.\n */\n function withdrawSwapFees(address tokenFeeIsIn) external onlyOwner {\n uint256 fee = tokenToSwapFees[tokenFeeIsIn];\n\n // Make sure there are actually fees to withdraw.\n if (fee == 0) revert LimitOrderRegistry__ZeroFeesToWithdraw(tokenFeeIsIn);\n\n tokenToSwapFees[tokenFeeIsIn] = 0;\n ERC20(tokenFeeIsIn).safeTransfer(owner, fee);\n }\n\n /**\n * @notice Allows owner to withdraw wrapped native and native assets from this contract.\n */\n function withdrawNative() external onlyOwner {\n uint256 wrappedNativeBalance = WRAPPED_NATIVE.balanceOf(address(this));\n uint256 nativeBalance = address(this).balance;\n // Make sure there is something to withdraw.\n if (wrappedNativeBalance == 0 && nativeBalance == 0) revert LimitOrderRegistry__ZeroNativeBalance();\n WRAPPED_NATIVE.safeTransfer(owner, WRAPPED_NATIVE.balanceOf(address(this)));\n payable(owner).transfer(address(this).balance);\n }\n\n /**\n * @notice Shutdown the cellar. Used in an emergency or if the cellar has been deprecated.\n */\n function initiateShutdown() external whenNotShutdown onlyOwner {\n isShutdown = true;\n\n emit ShutdownChanged(true);\n }\n\n /**\n * @notice Restart the cellar.\n */\n function liftShutdown() external onlyOwner {\n if (!isShutdown) revert LimitOrderRegistry__ContractNotShutdown();\n isShutdown = false;\n\n emit ShutdownChanged(false);\n }\n\n /*//////////////////////////////////////////////////////////////\n USER ORDER MANAGEMENT LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Creates a new limit order for a specific pool.\n * @dev Limit orders can be created to buy either token0, or token1 of the pool.\n * @param pool the Uniswap V3 pool to create a limit order on.\n * @param targetTick the tick, that when `pool`'s tick passes, the order will be completely fulfilled\n * @param amount the amount of the input token to sell for the desired token out\n * @param direction bool indicating what the desired token out is\n * - true token in = token0 ; token out = token1\n * - false token in = token1 ; token out = token0\n * @param startingNode an NFT position id indicating where this contract should start searching for a spot in the list\n * - can be zero which defaults to starting the search at center of list\n * @dev reverts if\n * - pool is not setup\n * - targetTick is not divisible by the pools tick spacing\n * - the new order would be ITM\n * - the new order does not meet minimum liquidity requirements\n * - transferFrom fails\n\n * @dev Emits a `NewOrder` event which contains meta data about the order including the orders `batchId`(which is used for claiming/cancelling).\n */\n function newOrder(\n UniswapV3Pool pool,\n int24 targetTick,\n uint128 amount,\n bool direction,\n uint256 startingNode\n ) external whenNotShutdown returns (uint128) {\n if (address(poolToData[pool].token0) == address(0)) revert LimitOrderRegistry__PoolNotSetup(address(pool));\n\n OrderDetails memory details;\n address sender = _msgSender();\n\n (, details.tick, , , , , ) = pool.slot0();\n\n // Determine upper and lower ticks.\n {\n int24 tickSpacing = pool.tickSpacing();\n // Make sure targetTick is divisible by spacing.\n if (targetTick % tickSpacing != 0) revert LimitOrderRegistry__InvalidTargetTick(targetTick, tickSpacing);\n if (direction) {\n details.upper = targetTick;\n details.lower = targetTick - tickSpacing;\n } else {\n details.upper = targetTick + tickSpacing;\n details.lower = targetTick;\n }\n }\n // Validate lower, upper,and direction.\n {\n OrderStatus status = _getOrderStatus(details.tick, details.lower, details.upper, direction);\n if (status != OrderStatus.OTM) revert LimitOrderRegistry__OrderITM(details.tick, targetTick, direction);\n }\n\n // Transfer assets into contract before setting any state.\n {\n ERC20 assetIn;\n if (direction) assetIn = poolToData[pool].token0;\n else assetIn = poolToData[pool].token1;\n _enforceMinimumLiquidity(amount, assetIn);\n assetIn.safeTransferFrom(sender, address(this), amount);\n }\n\n // Get the position id.\n details.positionId = getPositionFromTicks[details.lower][details.upper];\n\n if (direction) details.amount0 = amount;\n else details.amount1 = amount;\n if (details.positionId == 0) {\n // Create new LP position(which adds liquidity)\n PoolData memory data = poolToData[pool];\n details.positionId = _mintPosition(\n data,\n details.upper,\n details.lower,\n details.amount0,\n details.amount1,\n direction\n );\n\n // Add it to the list.\n _addPositionToList(data, startingNode, targetTick, details.positionId);\n\n // Set new orders upper and lower tick.\n orderBook[details.positionId].tickLower = details.lower;\n orderBook[details.positionId].tickUpper = details.upper;\n\n // Setup BatchOrder, setting batchId, direction.\n _setupOrder(direction, details.positionId);\n\n // Update token0Amount, token1Amount, batchIdToUserDepositAmount mapping.\n details.userTotal = _updateOrder(details.positionId, sender, amount);\n\n // Update the center values if need be.\n _updateCenter(pool, details.positionId, details.tick, details.upper, details.lower);\n\n // Update getPositionFromTicks since we have a new LP position.\n getPositionFromTicks[details.lower][details.upper] = details.positionId;\n } else {\n // Check if the position id is already being used in List.\n BatchOrder memory order = orderBook[details.positionId];\n if (order.token0Amount > 0 || order.token1Amount > 0) {\n // Need to add liquidity.\n PoolData memory data = poolToData[pool];\n _addToPosition(data, details.positionId, details.amount0, details.amount1, direction);\n\n // Update token0Amount, token1Amount, batchIdToUserDepositAmount mapping.\n details.userTotal = _updateOrder(details.positionId, sender, amount);\n } else {\n // We already have an LP position with given tick ranges, but it is not in linked list.\n PoolData memory data = poolToData[pool];\n\n // Add it to the list.\n _addPositionToList(data, startingNode, targetTick, details.positionId);\n\n // Setup BatchOrder, setting batchId, direction.\n _setupOrder(direction, details.positionId);\n\n // Need to add liquidity.\n _addToPosition(data, details.positionId, details.amount0, details.amount1, direction);\n\n // Update token0Amount, token1Amount, batchIdToUserDepositAmount mapping.\n details.userTotal = _updateOrder(details.positionId, sender, amount);\n\n // Update the center values if need be.\n _updateCenter(pool, details.positionId, details.tick, details.upper, details.lower);\n }\n }\n uint128 batchId = orderBook[details.positionId].batchId;\n emit NewOrder(sender, address(pool), amount, details.userTotal, orderBook[details.positionId]);\n return batchId;\n }\n\n /**\n * @notice Users can claim fulfilled orders by passing in the `batchId` corresponding to the order they want to claim.\n * @param batchId the batchId corresponding to a fulfilled order to claim\n * @param user the address of the user in the order to claim for\n * @dev Caller must either approve this contract to spend their Wrapped Native token, and have at least `getFeePerUser` tokens in their wallet.\n * Or caller must send `getFeePerUser` value with this call.\n */\n function claimOrder(uint128 batchId, address user) external payable returns (ERC20, uint256) {\n Claim storage userClaim = claim[batchId];\n if (!userClaim.isReadyForClaim) revert LimitOrderRegistry__OrderNotReadyToClaim(batchId);\n uint256 depositAmount = batchIdToUserDepositAmount[batchId][user];\n if (depositAmount == 0) revert LimitOrderRegistry__UserNotFound(user, batchId);\n\n // Zero out user balance.\n delete batchIdToUserDepositAmount[batchId][user];\n\n // Calculate owed amount.\n uint256 totalTokenDeposited;\n uint256 totalTokenOut;\n ERC20 tokenOut;\n if (userClaim.direction) {\n totalTokenDeposited = userClaim.token0Amount;\n totalTokenOut = userClaim.token1Amount;\n tokenOut = poolToData[userClaim.pool].token1;\n } else {\n totalTokenDeposited = userClaim.token1Amount;\n totalTokenOut = userClaim.token0Amount;\n tokenOut = poolToData[userClaim.pool].token0;\n }\n\n uint256 owed = (totalTokenOut * depositAmount) / totalTokenDeposited;\n\n // Transfer tokens owed to user.\n tokenOut.safeTransfer(user, owed);\n\n // Transfer fee in.\n address sender = _msgSender();\n if (msg.value >= userClaim.feePerUser) {\n // refund if necessary.\n uint256 refund = msg.value - userClaim.feePerUser;\n if (refund > 0) payable(sender).transfer(refund);\n } else {\n WRAPPED_NATIVE.safeTransferFrom(sender, address(this), userClaim.feePerUser);\n // If value is non zero send it back to caller.\n if (msg.value > 0) payable(sender).transfer(msg.value);\n }\n emit ClaimOrder(user, batchId, owed);\n return (tokenOut, owed);\n }\n\n /**\n * @notice Allows users to cancel orders as long as they are completely OTM.\n * @param pool the Uniswap V3 pool that contains the limit order to cancel\n * @param targetTick the targetTick of the order you want to cancel\n * @param direction bool indication the direction of the order\n * @dev This logic will send ALL the swap fees from a position to the last person that cancels the order.\n */\n function cancelOrder(\n UniswapV3Pool pool,\n int24 targetTick,\n bool direction\n ) external returns (uint128 amount0, uint128 amount1, uint128 batchId) {\n uint256 positionId;\n {\n // Make sure order is OTM.\n (, int24 tick, , , , , ) = pool.slot0();\n\n // Determine upper and lower ticks.\n int24 upper;\n int24 lower;\n {\n int24 tickSpacing = pool.tickSpacing();\n // Make sure targetTick is divisible by spacing.\n if (targetTick % tickSpacing != 0)\n revert LimitOrderRegistry__InvalidTargetTick(targetTick, tickSpacing);\n if (direction) {\n upper = targetTick;\n lower = targetTick - tickSpacing;\n } else {\n upper = targetTick + tickSpacing;\n lower = targetTick;\n }\n }\n // Validate lower, upper,and direction.\n {\n OrderStatus status = _getOrderStatus(tick, lower, upper, direction);\n if (status != OrderStatus.OTM) revert LimitOrderRegistry__OrderITM(tick, targetTick, direction);\n }\n\n // Get the position id.\n positionId = getPositionFromTicks[lower][upper];\n\n if (positionId == 0) revert LimitOrderRegistry__InvalidPositionId();\n }\n\n uint256 liquidityPercentToTake;\n\n // Get the users deposit amount in the order.\n BatchOrder storage order = orderBook[positionId];\n if (order.batchId == 0) revert LimitOrderRegistry__InvalidBatchId();\n address sender = _msgSender();\n {\n batchId = order.batchId;\n uint128 depositAmount = batchIdToUserDepositAmount[batchId][sender];\n if (depositAmount == 0) revert LimitOrderRegistry__UserNotFound(sender, batchId);\n\n // Remove one from the userCount.\n order.userCount--;\n\n // Zero out user balance.\n delete batchIdToUserDepositAmount[batchId][sender];\n\n uint128 orderAmount;\n if (order.direction) {\n orderAmount = order.token0Amount;\n if (orderAmount == depositAmount) {\n liquidityPercentToTake = 1e18;\n // Update order tokenAmount.\n order.token0Amount = 0;\n } else {\n liquidityPercentToTake = (1e18 * depositAmount) / orderAmount;\n // Update order tokenAmount.\n order.token0Amount = orderAmount - depositAmount;\n }\n } else {\n orderAmount = order.token1Amount;\n if (orderAmount == depositAmount) {\n liquidityPercentToTake = 1e18;\n // Update order tokenAmount.\n order.token1Amount = 0;\n } else {\n liquidityPercentToTake = (1e18 * depositAmount) / orderAmount;\n // Update order tokenAmount.\n order.token1Amount = orderAmount - depositAmount;\n }\n }\n\n (amount0, amount1) = _takeFromPosition(positionId, pool, liquidityPercentToTake);\n if (liquidityPercentToTake == 1e18) {\n _removeOrderFromList(positionId, pool, order);\n // Zero out balances for cancelled order.\n order.token0Amount = 0;\n order.token1Amount = 0;\n order.batchId = 0;\n }\n }\n if (order.direction) {\n if (amount0 > 0) poolToData[pool].token0.safeTransfer(sender, amount0);\n else revert LimitOrderRegistry__NoLiquidityInOrder();\n // Save any swap fees.\n if (amount1 > 0) tokenToSwapFees[address(poolToData[pool].token1)] += amount1;\n } else {\n if (amount1 > 0) poolToData[pool].token1.safeTransfer(sender, amount1);\n else revert LimitOrderRegistry__NoLiquidityInOrder();\n // Save any swap fees.\n if (amount0 > 0) tokenToSwapFees[address(poolToData[pool].token0)] += amount0;\n }\n emit CancelOrder(sender, amount0, amount1, order);\n }\n\n /*//////////////////////////////////////////////////////////////\n CHAINLINK AUTOMATION LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Returned `performData` simply contains a bool indicating which direction in the `orderBook` has orders that need to be fulfilled.\n */\n function checkUpkeep(bytes calldata checkData) external view returns (bool upkeepNeeded, bytes memory performData) {\n UniswapV3Pool pool = abi.decode(checkData, (UniswapV3Pool));\n (, int24 currentTick, , , , , ) = pool.slot0();\n PoolData memory data = poolToData[pool];\n BatchOrder memory order;\n OrderStatus status;\n bool walkDirection;\n\n if (data.centerHead != 0) {\n // centerHead is set, check if it is ITM.\n order = orderBook[data.centerHead];\n status = _getOrderStatus(currentTick, order.tickLower, order.tickUpper, order.direction);\n if (status == OrderStatus.ITM) {\n walkDirection = true; // Walk towards head of list.\n upkeepNeeded = true;\n performData = abi.encode(pool, walkDirection);\n return (upkeepNeeded, performData);\n }\n }\n if (data.centerTail != 0) {\n // If walk direction has not been set, then we know, no head orders are ITM.\n // So check tail orders.\n order = orderBook[data.centerTail];\n status = _getOrderStatus(currentTick, order.tickLower, order.tickUpper, order.direction);\n if (status == OrderStatus.ITM) {\n walkDirection = false; // Walk towards tail of list.\n upkeepNeeded = true;\n performData = abi.encode(pool, walkDirection);\n return (upkeepNeeded, performData);\n }\n }\n return (false, abi.encode(0));\n }\n\n /**\n * @notice Callable by anyone, as long as there are orders ITM, that need to be fulfilled.\n * @dev Does not use _removeOrderFromList, so that the center head/tail\n * value is not updated every single time and order is fulfilled, instead we just update it once at the end.\n */\n function performUpkeep(bytes calldata performData) external {\n (UniswapV3Pool pool, bool walkDirection) = abi.decode(performData, (UniswapV3Pool, bool));\n\n if (address(poolToData[pool].token0) == address(0)) revert LimitOrderRegistry__PoolNotSetup(address(pool));\n\n PoolData storage data = poolToData[pool];\n\n // Estimate gas cost.\n uint256 estimatedFee = uint256(upkeepGasLimit * getGasPrice());\n\n (, int24 currentTick, , , , , ) = pool.slot0();\n bool orderFilled;\n uint128 totalToken0Fees;\n uint128 totalToken1Fees;\n\n // Fulfill orders.\n uint256 target = walkDirection ? data.centerHead : data.centerTail;\n for (uint256 i; i < maxFillsPerUpkeep; ++i) {\n if (target == 0) break;\n BatchOrder storage order = orderBook[target];\n OrderStatus status = _getOrderStatus(currentTick, order.tickLower, order.tickUpper, order.direction);\n if (status == OrderStatus.ITM) {\n (uint128 token0Fees, uint128 token1Fees) = _fulfillOrder(target, pool, order, estimatedFee);\n totalToken0Fees += token0Fees;\n totalToken1Fees += token1Fees;\n target = walkDirection ? order.head : order.tail;\n // Zero out orders head and tail values removing order from the list.\n order.head = 0;\n order.tail = 0;\n // Update bool to indicate batch order is ready to handle claims.\n claim[order.batchId].isReadyForClaim = true;\n // Zero out orders batch id.\n order.batchId = 0;\n // Reset user count.\n order.userCount = 0;\n orderFilled = true;\n emit OrderFilled(order.batchId, address(pool));\n } else break;\n }\n\n if (!orderFilled) revert LimitOrderRegistry__NoOrdersToFulfill();\n\n // Save fees.\n if (totalToken0Fees > 0) tokenToSwapFees[address(poolToData[pool].token0)] += totalToken0Fees;\n if (totalToken1Fees > 0) tokenToSwapFees[address(poolToData[pool].token1)] += totalToken1Fees;\n\n // Update center.\n if (walkDirection) {\n data.centerHead = target;\n // Need to reconnect list.\n orderBook[data.centerTail].head = target;\n if (target != 0) orderBook[target].tail = data.centerTail;\n } else {\n data.centerTail = target;\n // Need to reconnect list.\n orderBook[data.centerHead].tail = target;\n if (target != 0) orderBook[target].head = data.centerHead;\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n INTERNAL ORDER LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Check if a given Uniswap V3 position is already in the `orderBook`.\n * @dev Looks at Nodes head and tail, and checks for edge case of node being the only node in the `orderBook`\n */\n function _checkThatNodeIsInList(uint256 node, BatchOrder memory order, PoolData memory data) internal pure {\n if (order.head == 0 && order.tail == 0) {\n // Possible but the order may be centerTail or centerHead.\n if (data.centerHead != node && data.centerTail != node) revert LimitOrderRegistry__OrderNotInList(node);\n }\n }\n\n /**\n * @notice Finds appropriate spot in `orderBook` for an order.\n */\n function _findSpot(\n PoolData memory data,\n uint256 startingNode,\n int24 targetTick\n ) internal view returns (uint256 proposedHead, uint256 proposedTail) {\n BatchOrder memory node;\n if (startingNode == 0) {\n if (data.centerHead != 0) {\n startingNode = data.centerHead;\n node = orderBook[startingNode];\n } else if (data.centerTail != 0) {\n startingNode = data.centerTail;\n node = orderBook[startingNode];\n } else return (0, 0);\n } else {\n node = orderBook[startingNode];\n _checkThatNodeIsInList(startingNode, node, data);\n }\n uint256 nodeId = startingNode;\n bool direction = targetTick > node.tickUpper ? true : false;\n while (true) {\n if (direction) {\n // Go until we find an order with a tick lower GREATER or equal to targetTick, then set proposedTail equal to the tail, and proposed head to the current node.\n if (node.tickLower >= targetTick) {\n return (nodeId, node.tail);\n } else if (node.head == 0) {\n // Made it to head of list.\n return (0, nodeId);\n } else {\n nodeId = node.head;\n node = orderBook[nodeId];\n }\n } else {\n // Go until we find tick upper that is LESS than or equal to targetTick\n if (node.tickUpper <= targetTick) {\n return (node.head, nodeId);\n } else if (node.tail == 0) {\n // Made it to the tail of the list.\n return (nodeId, 0);\n } else {\n nodeId = node.tail;\n node = orderBook[nodeId];\n }\n }\n }\n }\n\n /**\n * @notice Checks if newly added order should be made the new center head/tail.\n */\n function _updateCenter(\n UniswapV3Pool pool,\n uint256 positionId,\n int24 currentTick,\n int24 upper,\n int24 lower\n ) internal {\n PoolData memory data = poolToData[pool];\n if (currentTick > upper) {\n // Check if centerTail needs to be updated.\n if (data.centerTail == 0) {\n // Currently no centerTail, so this order must become it.\n poolToData[pool].centerTail = positionId;\n } else {\n BatchOrder memory centerTail = orderBook[data.centerTail];\n if (upper > centerTail.tickUpper) {\n // New position is closer to the current pool tick, so it becomes new centerTail.\n poolToData[pool].centerTail = positionId;\n }\n // else nothing to do.\n }\n } else if (currentTick < lower) {\n // Check if centerHead needs to be updated.\n if (data.centerHead == 0) {\n // Currently no centerHead, so this order must become it.\n poolToData[pool].centerHead = positionId;\n } else {\n BatchOrder memory centerHead = orderBook[data.centerHead];\n if (lower < centerHead.tickLower) {\n // New position is closer to the current pool tick, so it becomes new centerHead.\n poolToData[pool].centerHead = positionId;\n }\n // else nothing to do.\n }\n }\n }\n\n /**\n * @notice Add a Uniswap V3 LP position to the `orderBook`.\n */\n function _addPositionToList(\n PoolData memory data,\n uint256 startingNode,\n int24 targetTick,\n uint256 position\n ) internal {\n (uint256 head, uint256 tail) = _findSpot(data, startingNode, targetTick);\n if (tail != 0) {\n orderBook[tail].head = position;\n orderBook[position].tail = tail;\n }\n if (head != 0) {\n orderBook[head].tail = position;\n orderBook[position].head = head;\n }\n }\n\n /**\n * @notice Setup a newly minted LP position, or one being reused.\n * @dev Sets batchId, and direction.\n */\n function _setupOrder(bool direction, uint256 position) internal {\n BatchOrder storage order = orderBook[position];\n order.batchId = batchCount;\n order.direction = direction;\n batchCount++;\n }\n\n /**\n * @notice Updates a BatchOrder's token0/token1 amount, as well as associated\n * `batchIdToUserDepositAmount` mapping value.\n * @dev If user is new to the order, increment userCount.\n */\n function _updateOrder(uint256 positionId, address user, uint128 amount) internal returns (uint128 userTotal) {\n BatchOrder storage order = orderBook[positionId];\n if (order.direction) {\n // token1\n order.token0Amount += amount;\n } else {\n // token0\n order.token1Amount += amount;\n }\n\n // Check if user is already in the order.\n uint128 batchId = order.batchId;\n uint128 originalDepositAmount = batchIdToUserDepositAmount[batchId][user];\n // If this is a new user in the order, add 1 to userCount.\n if (originalDepositAmount == 0) order.userCount++;\n batchIdToUserDepositAmount[batchId][user] = originalDepositAmount + amount;\n return (originalDepositAmount + amount);\n }\n\n /**\n * @notice Mints a new Uniswap V3 LP position.\n */\n function _mintPosition(\n PoolData memory data,\n int24 upper,\n int24 lower,\n uint128 amount0,\n uint128 amount1,\n bool direction\n ) internal returns (uint256) {\n if (direction) data.token0.safeApprove(address(POSITION_MANAGER), amount0);\n else data.token1.safeApprove(address(POSITION_MANAGER), amount1);\n\n // 0.9999e18 accounts for rounding errors in the Uniswap V3 protocol.\n uint128 amount0Min = amount0 == 0 ? 0 : (amount0 * 0.9999e18) / 1e18;\n uint128 amount1Min = amount1 == 0 ? 0 : (amount1 * 0.9999e18) / 1e18;\n\n // Create mint params.\n NonfungiblePositionManager.MintParams memory params = NonfungiblePositionManager.MintParams({\n token0: address(data.token0),\n token1: address(data.token1),\n fee: data.fee,\n tickLower: lower,\n tickUpper: upper,\n amount0Desired: amount0,\n amount1Desired: amount1,\n amount0Min: amount0Min,\n amount1Min: amount1Min,\n recipient: address(this),\n deadline: block.timestamp\n });\n\n // Supply liquidity to pool.\n (uint256 tokenId, , , ) = POSITION_MANAGER.mint(params);\n\n // Revert if tokenId received is 0 id.\n // Zero token id is reserved for NULL values in linked list.\n if (tokenId == 0) revert LimitOrderRegistry__InvalidPositionId();\n\n // If position manager still has allowance, zero it out.\n if (direction && data.token0.allowance(address(this), address(POSITION_MANAGER)) > 0)\n data.token0.safeApprove(address(POSITION_MANAGER), 0);\n if (!direction && data.token1.allowance(address(this), address(POSITION_MANAGER)) > 0)\n data.token1.safeApprove(address(POSITION_MANAGER), 0);\n\n return tokenId;\n }\n\n /**\n * @notice Adds liquidity to a given `positionId`.\n */\n function _addToPosition(\n PoolData memory data,\n uint256 positionId,\n uint128 amount0,\n uint128 amount1,\n bool direction\n ) internal {\n if (direction) data.token0.safeApprove(address(POSITION_MANAGER), amount0);\n else data.token1.safeApprove(address(POSITION_MANAGER), amount1);\n\n uint128 amount0Min = amount0 == 0 ? 0 : (amount0 * 0.9999e18) / 1e18;\n uint128 amount1Min = amount1 == 0 ? 0 : (amount1 * 0.9999e18) / 1e18;\n\n // Create increase liquidity params.\n NonfungiblePositionManager.IncreaseLiquidityParams memory params = NonfungiblePositionManager\n .IncreaseLiquidityParams({\n tokenId: positionId,\n amount0Desired: amount0,\n amount1Desired: amount1,\n amount0Min: amount0Min,\n amount1Min: amount1Min,\n deadline: block.timestamp\n });\n\n // Increase liquidity in pool.\n POSITION_MANAGER.increaseLiquidity(params);\n\n // If position manager still has allowance, zero it out.\n if (direction && data.token0.allowance(address(this), address(POSITION_MANAGER)) > 0)\n data.token0.safeApprove(address(POSITION_MANAGER), 0);\n if (!direction && data.token1.allowance(address(this), address(POSITION_MANAGER)) > 0)\n data.token1.safeApprove(address(POSITION_MANAGER), 0);\n }\n\n /**\n * @notice Enforces minimum liquidity requirements for orders.\n */\n function _enforceMinimumLiquidity(uint256 amount, ERC20 asset) internal view {\n uint256 minimum = minimumAssets[asset];\n if (minimum == 0) revert LimitOrderRegistry__MinimumNotSet(address(asset));\n if (amount < minimum) revert LimitOrderRegistry__MinimumNotMet(address(asset), minimum, amount);\n }\n\n /**\n * @notice Helper function to determine an orders status.\n * @dev Returns\n * - ITM if order is ready to be filled, and is composed of wanted asset\n * - OTM if order is not ready to be filled, but order can still be cancelled, because order is composed of asset to sell\n * - MIXED order is composed of both wanted asset, and asset to sell, can not be fulfilled or cancelled.\n */\n function _getOrderStatus(\n int24 currentTick,\n int24 lower,\n int24 upper,\n bool direction\n ) internal pure returns (OrderStatus status) {\n if (upper == lower) revert LimitOrderRegistry__InvalidTickRange(upper, lower);\n if (direction) {\n // Indicates we want to go lower -> upper.\n if (currentTick > upper) return OrderStatus.ITM;\n if (currentTick >= lower) return OrderStatus.MIXED;\n else return OrderStatus.OTM;\n } else {\n // Indicates we want to go upper -> lower.\n if (currentTick < lower) return OrderStatus.ITM;\n if (currentTick <= upper) return OrderStatus.MIXED;\n else return OrderStatus.OTM;\n }\n }\n\n /**\n * @notice Called during `performUpkeep` to fulfill an ITM order.\n * @dev Sets Claim info, removes all liquidity from position, and zeroes out BatchOrder amount0 and amount1 values.\n */\n function _fulfillOrder(\n uint256 target,\n UniswapV3Pool pool,\n BatchOrder storage order,\n uint256 estimatedFee\n ) internal returns (uint128 token0Fees, uint128 token1Fees) {\n // Save fee per user in Claim Struct.\n uint256 totalUsers = order.userCount;\n Claim storage newClaim = claim[order.batchId];\n newClaim.feePerUser = uint128(estimatedFee / totalUsers);\n newClaim.pool = pool;\n\n // Take all liquidity from the order.\n (uint128 amount0, uint128 amount1) = _takeFromPosition(target, pool, 1e18);\n if (order.direction) {\n // Copy the tokenIn amount from the order, this is the total user deposit.\n newClaim.token0Amount = order.token0Amount;\n // Total amount received is the difference in balance.\n newClaim.token1Amount = amount1;\n\n // Record any extra swap fees pool earned.\n token0Fees = amount0;\n } else {\n // Copy the tokenIn amount from the order, this is the total user deposit.\n newClaim.token1Amount = order.token1Amount;\n // Total amount received is the difference in balance.\n newClaim.token0Amount = amount0;\n\n // Record any extra swap fees pool earned.\n token1Fees = amount1;\n }\n newClaim.direction = order.direction;\n\n // Zero out order balances.\n order.token0Amount = 0;\n order.token1Amount = 0;\n }\n\n /**\n * @notice Removes liquidity from `target` Uniswap V3 LP position.\n * @dev Collects fees from `target` position.\n */\n function _takeFromPosition(\n uint256 target,\n UniswapV3Pool pool,\n uint256 liquidityPercent\n ) internal returns (uint128, uint128) {\n (, , , , , , , uint128 liquidity, , , , ) = POSITION_MANAGER.positions(target);\n liquidity = uint128(uint256(liquidity * liquidityPercent) / 1e18);\n\n // Create decrease liquidity params.\n NonfungiblePositionManager.DecreaseLiquidityParams memory params = NonfungiblePositionManager\n .DecreaseLiquidityParams({\n tokenId: target,\n liquidity: liquidity,\n amount0Min: 0,\n amount1Min: 0,\n deadline: block.timestamp\n });\n\n // Decrease liquidity in pool.\n uint128 amount0;\n uint128 amount1;\n {\n (uint256 a0, uint256 a1) = POSITION_MANAGER.decreaseLiquidity(params);\n amount0 = uint128(a0);\n amount1 = uint128(a1);\n }\n\n // If completely closing position, then collect fees as well.\n uint128 amount0Max;\n uint128 amount1Max;\n if (liquidityPercent == 1e18) {\n amount0Max = type(uint128).max;\n amount1Max = type(uint128).max;\n } else {\n // Otherwise only collect principal.\n amount0Max = amount0;\n amount1Max = amount1;\n }\n // Create fee collection params.\n NonfungiblePositionManager.CollectParams memory collectParams = NonfungiblePositionManager.CollectParams({\n tokenId: target,\n recipient: address(this),\n amount0Max: amount0Max,\n amount1Max: amount1Max\n });\n\n // Save token balances.\n ERC20 token0 = poolToData[pool].token0;\n ERC20 token1 = poolToData[pool].token1;\n uint256 token0Balance = token0.balanceOf(address(this));\n uint256 token1Balance = token1.balanceOf(address(this));\n\n // Collect fees.\n POSITION_MANAGER.collect(collectParams);\n\n amount0 = uint128(token0.balanceOf(address(this)) - token0Balance);\n amount1 = uint128(token1.balanceOf(address(this)) - token1Balance);\n\n return (amount0, amount1);\n }\n\n /**\n * @notice Removes an order from the `orderBook`.\n * @dev Checks if order is one of the center values, and updates the head if need be.\n */\n function _removeOrderFromList(uint256 target, UniswapV3Pool pool, BatchOrder storage order) internal {\n // Checks if order is the center, if so then it will set it to the the center orders head(which is okay if it is zero).\n uint256 centerHead = poolToData[pool].centerHead;\n uint256 centerTail = poolToData[pool].centerTail;\n\n if (target == centerHead) {\n uint256 newHead = orderBook[centerHead].head;\n poolToData[pool].centerHead = newHead;\n } else if (target == centerTail) {\n uint256 newTail = orderBook[centerTail].tail;\n poolToData[pool].centerTail = newTail;\n }\n\n // Remove order from linked list.\n orderBook[order.tail].head = order.head;\n orderBook[order.head].tail = order.tail;\n order.head = 0;\n order.tail = 0;\n }\n\n /**\n * @notice Helper function to get the gas price used for fee calculation.\n */\n function getGasPrice() public view returns (uint256) {\n // If gas feed is set use it.\n if (fastGasFeed != address(0)) return uint256(IChainlinkAggregator(fastGasFeed).latestAnswer());\n // Else use owner set value.\n return uint256(upkeepGasPrice) * 1e9; // Multiply by 1e9 to convert gas price to gwei\n }\n\n /*//////////////////////////////////////////////////////////////\n VIEW LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Helper function that finds the appropriate spot in the linked list for a new order.\n * @param pool the Uniswap V3 pool you want to create an order in\n * @param startingNode the UniV3 position Id to start looking\n * @param targetTick the targetTick of the order you want to place\n * @return proposedHead , proposedTail pr the correct head and tail for the new order\n * @dev if both head and tail are zero, just pass in zero for the `startingNode`\n * otherwise pass in either the nonzero head or nonzero tail for the `startingNode`\n */\n function findSpot(\n UniswapV3Pool pool,\n uint256 startingNode,\n int24 targetTick\n ) external view returns (uint256 proposedHead, uint256 proposedTail) {\n PoolData memory data = poolToData[pool];\n\n int24 tickSpacing = pool.tickSpacing();\n // Make sure targetTick is divisible by spacing.\n if (targetTick % tickSpacing != 0) revert LimitOrderRegistry__InvalidTargetTick(targetTick, tickSpacing);\n\n (proposedHead, proposedTail) = _findSpot(data, startingNode, targetTick);\n }\n\n /**\n * @notice Helper function to get the fee per user for a specific order.\n */\n function getFeePerUser(uint128 batchId) external view returns (uint128) {\n return claim[batchId].feePerUser;\n }\n\n /**\n * @notice Helper function to view if a BatchOrder is ready to claim.\n */\n function isOrderReadyForClaim(uint128 batchId) external view returns (bool) {\n return claim[batchId].isReadyForClaim;\n }\n\n function getOrderBook(uint256 id) external view returns (BatchOrder memory) {\n return orderBook[id];\n }\n\n function getClaim(uint128 batchId) external view returns (Claim memory) {\n return claim[batchId];\n }\n}\n"},"src/LimitOrderRegistryLens.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.16;\n\nimport { UniswapV3Pool } from \"src/interfaces/uniswapV3/UniswapV3Pool.sol\";\nimport { LimitOrderRegistry } from \"src/LimitOrderRegistry.sol\";\n\n/**\n * @title Limit Order Registry Lens\n * @notice Stores additional view functions for limit order registry.\n * @author crispymangoes\n */\ncontract LimitOrderRegistryLens {\n /*//////////////////////////////////////////////////////////////\n STRUCTS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Stores batch order information and underlying LP position token id.\n * @param id the underling LP position token id\n * @param batchOrder see BatchOrder above\n */\n struct BatchOrderViewData {\n uint256 id;\n LimitOrderRegistry.BatchOrder batchOrder;\n }\n\n LimitOrderRegistry public registry;\n\n constructor(LimitOrderRegistry _registry) {\n registry = _registry;\n }\n\n /*//////////////////////////////////////////////////////////////\n VIEW LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Walks the `orderBook` in a specific `direction`, returning an array of BatchOrderViewData with length of up to `returnCount`.\n * @param pool UniswapV3 pool whose order book you want to query\n * @param startingNode the node to start walking from\n * @param returnCount the max number of values in return array\n * @param direction to walk the order book\n */\n function walkOrders(\n UniswapV3Pool pool,\n uint256 startingNode,\n uint256 returnCount,\n bool direction\n ) external view returns (BatchOrderViewData[] memory orders) {\n orders = new BatchOrderViewData[](returnCount);\n (uint256 centerHead, uint256 centerTail, , , ) = registry.poolToData(pool);\n if (direction) {\n // Walk toward head.\n uint256 targetId = startingNode == 0 ? centerHead : startingNode;\n LimitOrderRegistry.BatchOrder memory target = registry.getOrderBook(targetId);\n for (uint256 i; i < returnCount; ++i) {\n orders[i] = BatchOrderViewData({ id: targetId, batchOrder: target });\n targetId = target.head;\n if (targetId != 0) target = registry.getOrderBook(targetId);\n else break;\n }\n } else {\n // Walk toward tail.\n uint256 targetId = startingNode == 0 ? centerTail : startingNode;\n LimitOrderRegistry.BatchOrder memory target = registry.getOrderBook(targetId);\n for (uint256 i; i < returnCount; ++i) {\n orders[i] = BatchOrderViewData({ id: targetId, batchOrder: target });\n targetId = target.tail;\n if (targetId != 0) target = registry.getOrderBook(targetId);\n else break;\n }\n }\n }\n\n /**\n * @notice Helper function that finds the appropriate spot in the linked list for a new order.\n * @param pool the Uniswap V3 pool you want to create an order in\n * @param startingNode the UniV3 position Id to start looking\n * @param targetTick the targetTick of the order you want to place\n * @return proposedHead , proposedTail pr the correct head and tail for the new order\n * @dev if both head and tail are zero, just pass in zero for the `startingNode`\n * otherwise pass in either the nonzero head or nonzero tail for the `startingNode`\n */\n function findSpot(\n UniswapV3Pool pool,\n uint256 startingNode,\n int24 targetTick\n ) external view returns (uint256 proposedHead, uint256 proposedTail) {\n (proposedHead, proposedTail) = registry.findSpot(pool, startingNode, targetTick);\n }\n\n /**\n * @notice Helper function to get the fee per user for a specific order.\n */\n function getFeePerUser(uint128 batchId) external view returns (uint128) {\n return registry.getClaim(batchId).feePerUser;\n }\n\n /**\n * @notice Helper function to view if a BatchOrder is ready to claim.\n */\n function isOrderReadyForClaim(uint128 batchId) external view returns (bool) {\n return registry.getClaim(batchId).isReadyForClaim;\n }\n}\n"},"src/interfaces/chainlink/IChainlinkAggregator.sol":{"content":"// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.16;\n\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol\";\n\ninterface IChainlinkAggregator is AggregatorV2V3Interface {\n function maxAnswer() external view returns (int192);\n\n function minAnswer() external view returns (int192);\n\n function aggregator() external view returns (address);\n}\n"},"src/interfaces/chainlink/IKeeperRegistrar.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.16;\n\nstruct RegistrationParams {\n string name;\n bytes encryptedEmail;\n address upkeepContract;\n uint32 gasLimit;\n address adminAddress;\n bytes checkData;\n bytes offchainConfig;\n uint96 amount;\n}\n\n/**\n * @notice Contract to accept requests for upkeep registrations\n * @dev There are 2 registration workflows in this contract\n * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually\n * calls `approve` to register upkeep and emit events to inform UI and others interested.\n * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on\n * keeper registry and then emits approved event to finish the flow automatically without manual intervention.\n * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not.\n * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations.\n */\ninterface IKeeperRegistrar {\n /**\n * @notice register can only be called through transferAndCall on LINK contract\n * @param name string of the upkeep to be registered\n * @param encryptedEmail email address of upkeep contact\n * @param upkeepContract address to perform upkeep on\n * @param gasLimit amount of gas to provide the target contract when performing upkeep\n * @param adminAddress address to cancel upkeep and withdraw remaining funds\n * @param checkData data passed to the contract when checking for upkeep\n * @param amount quantity of LINK upkeep is funded with (specified in Juels)\n * @param source application sending this request\n * @param sender address of the sender making the request\n */\n function register(\n string memory name,\n bytes calldata encryptedEmail,\n address upkeepContract,\n uint32 gasLimit,\n address adminAddress,\n bytes calldata checkData,\n uint96 amount,\n uint8 source,\n address sender\n ) external;\n\n function registerUpkeep(RegistrationParams calldata requestParams) external returns (uint256);\n}\n"},"src/interfaces/uniswapV3/NonfungiblePositionManager.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface NonfungiblePositionManager {\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1);\n event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);\n event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n struct CollectParams {\n uint256 tokenId;\n address recipient;\n uint128 amount0Max;\n uint128 amount1Max;\n }\n\n struct DecreaseLiquidityParams {\n uint256 tokenId;\n uint128 liquidity;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n struct IncreaseLiquidityParams {\n uint256 tokenId;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n uint256 deadline;\n }\n\n struct MintParams {\n address token0;\n address token1;\n uint24 fee;\n int24 tickLower;\n int24 tickUpper;\n uint256 amount0Desired;\n uint256 amount1Desired;\n uint256 amount0Min;\n uint256 amount1Min;\n address recipient;\n uint256 deadline;\n }\n\n function DOMAIN_SEPARATOR() external view returns (bytes32);\n\n function PERMIT_TYPEHASH() external view returns (bytes32);\n\n function WETH9() external view returns (address);\n\n function approve(address to, uint256 tokenId) external;\n\n function balanceOf(address owner) external view returns (uint256);\n\n function baseURI() external pure returns (string memory);\n\n function burn(uint256 tokenId) external payable;\n\n function collect(CollectParams memory params) external payable returns (uint256 amount0, uint256 amount1);\n\n function createAndInitializePoolIfNecessary(\n address token0,\n address token1,\n uint24 fee,\n uint160 sqrtPriceX96\n ) external payable returns (address pool);\n\n function decreaseLiquidity(\n DecreaseLiquidityParams memory params\n ) external payable returns (uint256 amount0, uint256 amount1);\n\n function factory() external view returns (address);\n\n function getApproved(uint256 tokenId) external view returns (address);\n\n function increaseLiquidity(\n IncreaseLiquidityParams memory params\n ) external payable returns (uint128 liquidity, uint256 amount0, uint256 amount1);\n\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n function mint(\n MintParams memory params\n ) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);\n\n function multicall(bytes[] memory data) external payable returns (bytes[] memory results);\n\n function name() external view returns (string memory);\n\n function ownerOf(uint256 tokenId) external view returns (address);\n\n function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable;\n\n function positions(\n uint256 tokenId\n )\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n uint24 fee,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n\n function refundETH() external payable;\n\n function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) external;\n\n function selfPermit(address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable;\n\n function selfPermitAllowed(\n address token,\n uint256 nonce,\n uint256 expiry,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external payable;\n\n function selfPermitAllowedIfNecessary(\n address token,\n uint256 nonce,\n uint256 expiry,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external payable;\n\n function selfPermitIfNecessary(\n address token,\n uint256 value,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external payable;\n\n function setApprovalForAll(address operator, bool approved) external;\n\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n\n function sweepToken(address token, uint256 amountMinimum, address recipient) external payable;\n\n function symbol() external view returns (string memory);\n\n function tokenByIndex(uint256 index) external view returns (uint256);\n\n function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);\n\n function tokenURI(uint256 tokenId) external view returns (string memory);\n\n function totalSupply() external view returns (uint256);\n\n function transferFrom(address from, address to, uint256 tokenId) external;\n\n function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes memory data) external;\n\n function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;\n}\n"},"src/interfaces/uniswapV3/UniswapV3Pool.sol":{"content":"pragma solidity ^0.8.10;\n\ninterface UniswapV3Pool {\n event Burn(\n address indexed owner,\n int24 indexed tickLower,\n int24 indexed tickUpper,\n uint128 amount,\n uint256 amount0,\n uint256 amount1\n );\n event Collect(\n address indexed owner,\n address recipient,\n int24 indexed tickLower,\n int24 indexed tickUpper,\n uint128 amount0,\n uint128 amount1\n );\n event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);\n event Flash(\n address indexed sender,\n address indexed recipient,\n uint256 amount0,\n uint256 amount1,\n uint256 paid0,\n uint256 paid1\n );\n event IncreaseObservationCardinalityNext(\n uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew\n );\n event Initialize(uint160 sqrtPriceX96, int24 tick);\n event Mint(\n address sender,\n address indexed owner,\n int24 indexed tickLower,\n int24 indexed tickUpper,\n uint128 amount,\n uint256 amount0,\n uint256 amount1\n );\n event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);\n event Swap(\n address indexed sender,\n address indexed recipient,\n int256 amount0,\n int256 amount1,\n uint160 sqrtPriceX96,\n uint128 liquidity,\n int24 tick\n );\n\n function burn(int24 tickLower, int24 tickUpper, uint128 amount)\n external\n returns (uint256 amount0, uint256 amount1);\n function collect(\n address recipient,\n int24 tickLower,\n int24 tickUpper,\n uint128 amount0Requested,\n uint128 amount1Requested\n ) external returns (uint128 amount0, uint128 amount1);\n function collectProtocol(address recipient, uint128 amount0Requested, uint128 amount1Requested)\n external\n returns (uint128 amount0, uint128 amount1);\n function factory() external view returns (address);\n function fee() external view returns (uint24);\n function feeGrowthGlobal0X128() external view returns (uint256);\n function feeGrowthGlobal1X128() external view returns (uint256);\n function flash(address recipient, uint256 amount0, uint256 amount1, bytes memory data) external;\n function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;\n function initialize(uint160 sqrtPriceX96) external;\n function liquidity() external view returns (uint128);\n function maxLiquidityPerTick() external view returns (uint128);\n function mint(address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes memory data)\n external\n returns (uint256 amount0, uint256 amount1);\n function observations(uint256)\n external\n view\n returns (\n uint32 blockTimestamp,\n int56 tickCumulative,\n uint160 secondsPerLiquidityCumulativeX128,\n bool initialized\n );\n function observe(uint32[] memory secondsAgos)\n external\n view\n returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);\n function positions(bytes32)\n external\n view\n returns (\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n function protocolFees() external view returns (uint128 token0, uint128 token1);\n function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;\n function slot0()\n external\n view\n returns (\n uint160 sqrtPriceX96,\n int24 tick,\n uint16 observationIndex,\n uint16 observationCardinality,\n uint16 observationCardinalityNext,\n uint8 feeProtocol,\n bool unlocked\n );\n function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)\n external\n view\n returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);\n function swap(\n address recipient,\n bool zeroForOne,\n int256 amountSpecified,\n uint160 sqrtPriceLimitX96,\n bytes memory data\n ) external returns (int256 amount0, int256 amount1);\n function tickBitmap(int16) external view returns (uint256);\n function tickSpacing() external view returns (int24);\n function ticks(int24)\n external\n view\n returns (\n uint128 liquidityGross,\n int128 liquidityNet,\n uint256 feeGrowthOutside0X128,\n uint256 feeGrowthOutside1X128,\n int56 tickCumulativeOutside,\n uint160 secondsPerLiquidityOutsideX128,\n uint32 secondsOutside,\n bool initialized\n );\n function token0() external view returns (address);\n function token1() external view returns (address);\n}\n"}},"settings":{"remappings":["@chainlink/=lib/chainlink/","@ds-test/=lib/forge-std/lib/ds-test/src/","@forge-std/=lib/forge-std/src/","@openzeppelin/=lib/openzeppelin-contracts/","@solmate/=lib/solmate/src/","chainlink/=lib/chainlink/integration-tests/contracts/ethereum/src/","ds-test/=lib/forge-std/lib/ds-test/src/","forge-std/=lib/forge-std/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/","solmate/=lib/solmate/src/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"outputSelection":{"*":{"":["ast"],"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"london","libraries":{}}}