generated from crispymangoes/forge-template
-
Notifications
You must be signed in to change notification settings - Fork 8
/
TradeManagerFactoryVerification.json
1 lines (1 loc) · 137 KB
/
TradeManagerFactoryVerification.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/proxy/Clones.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for\n * deploying minimal proxy contracts, also known as \"clones\".\n *\n * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies\n * > a minimal bytecode implementation that delegates all calls to a known, fixed address.\n *\n * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`\n * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the\n * deterministic method.\n *\n * _Available since v3.4._\n */\nlibrary Clones {\n /**\n * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n *\n * This function uses the create opcode, which should never revert.\n */\n function clone(address implementation) internal returns (address instance) {\n /// @solidity memory-safe-assembly\n assembly {\n // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n // of the `implementation` address with the bytecode before the address.\n mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n instance := create(0, 0x09, 0x37)\n }\n require(instance != address(0), \"ERC1167: create failed\");\n }\n\n /**\n * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n *\n * This function uses the create2 opcode and a `salt` to deterministically deploy\n * the clone. Using the same `implementation` and `salt` multiple time will revert, since\n * the clones cannot be deployed twice at the same address.\n */\n function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {\n /// @solidity memory-safe-assembly\n assembly {\n // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n // of the `implementation` address with the bytecode before the address.\n mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n instance := create2(0, 0x09, 0x37, salt)\n }\n require(instance != address(0), \"ERC1167: create2 failed\");\n }\n\n /**\n * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n */\n function predictDeterministicAddress(\n address implementation,\n bytes32 salt,\n address deployer\n ) internal pure returns (address predicted) {\n /// @solidity memory-safe-assembly\n assembly {\n let ptr := mload(0x40)\n mstore(add(ptr, 0x38), deployer)\n mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)\n mstore(add(ptr, 0x14), implementation)\n mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)\n mstore(add(ptr, 0x58), salt)\n mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))\n predicted := keccak256(add(ptr, 0x43), 0x55)\n }\n }\n\n /**\n * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n */\n function predictDeterministicAddress(address implementation, bytes32 salt)\n internal\n view\n returns (address predicted)\n {\n return predictDeterministicAddress(implementation, salt, address(this));\n }\n}\n"},"lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (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. Equivalent to `reinitializer(1)`.\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 * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original\n * initialization step. This is essential to configure modules that are added through upgrades and that require\n * initialization.\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 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 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 Internal function that returns the initialized version. Returns `_initialized`\n */\n function _getInitializedVersion() internal view returns (uint8) {\n return _initialized;\n }\n\n /**\n * @dev Internal function that returns the initialized version. Returns `_initializing`\n */\n function _isInitializing() internal view returns (bool) {\n return _initializing;\n }\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/Address.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResultFromTarget(target, success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling\n * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.\n *\n * _Available since v4.8._\n */\n function verifyCallResultFromTarget(\n address target,\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n if (success) {\n if (returndata.length == 0) {\n // only check isContract if the call was successful and the return data is empty\n // otherwise we already know that it was a contract\n require(isContract(target), \"Address: call to non-contract\");\n }\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n /**\n * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason or using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n _revert(returndata, errorMessage);\n }\n }\n\n function _revert(bytes memory returndata, string memory errorMessage) private pure {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n /// @solidity memory-safe-assembly\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n}\n"},"lib/openzeppelin-contracts/contracts/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/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)\n// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n * // Add the library methods\n * using EnumerableSet for EnumerableSet.AddressSet;\n *\n * // Declare a set state variable\n * EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n *\n * [WARNING]\n * ====\n * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure\n * unusable.\n * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.\n *\n * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an\n * array of EnumerableSet.\n * ====\n */\nlibrary EnumerableSet {\n // To implement this library for multiple types with as little code\n // repetition as possible, we write it in terms of a generic Set type with\n // bytes32 values.\n // The Set implementation uses private functions, and user-facing\n // implementations (such as AddressSet) are just wrappers around the\n // underlying Set.\n // This means that we can only create new EnumerableSets for types that fit\n // in bytes32.\n\n struct Set {\n // Storage of set values\n bytes32[] _values;\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(bytes32 => uint256) _indexes;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function _add(Set storage set, bytes32 value) private returns (bool) {\n if (!_contains(set, value)) {\n set._values.push(value);\n // The value is stored at length-1, but we add 1 to all indexes\n // and use 0 as a sentinel value\n set._indexes[value] = set._values.length;\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function _remove(Set storage set, bytes32 value) private returns (bool) {\n // We read and store the value's index to prevent multiple reads from the same storage slot\n uint256 valueIndex = set._indexes[value];\n\n if (valueIndex != 0) {\n // Equivalent to contains(set, value)\n // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n // the array, and then remove the last element (sometimes called as 'swap and pop').\n // This modifies the order of the array, as noted in {at}.\n\n uint256 toDeleteIndex = valueIndex - 1;\n uint256 lastIndex = set._values.length - 1;\n\n if (lastIndex != toDeleteIndex) {\n bytes32 lastValue = set._values[lastIndex];\n\n // Move the last value to the index where the value to delete is\n set._values[toDeleteIndex] = lastValue;\n // Update the index for the moved value\n set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex\n }\n\n // Delete the slot where the moved value was stored\n set._values.pop();\n\n // Delete the index for the deleted slot\n delete set._indexes[value];\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function _contains(Set storage set, bytes32 value) private view returns (bool) {\n return set._indexes[value] != 0;\n }\n\n /**\n * @dev Returns the number of values on the set. O(1).\n */\n function _length(Set storage set) private view returns (uint256) {\n return set._values.length;\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function _at(Set storage set, uint256 index) private view returns (bytes32) {\n return set._values[index];\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function _values(Set storage set) private view returns (bytes32[] memory) {\n return set._values;\n }\n\n // Bytes32Set\n\n struct Bytes32Set {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _add(set._inner, value);\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n return _remove(set._inner, value);\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return _contains(set._inner, value);\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return _at(set._inner, index);\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {\n bytes32[] memory store = _values(set._inner);\n bytes32[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // AddressSet\n\n struct AddressSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n return _add(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n return _remove(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return _contains(set._inner, bytes32(uint256(uint160(value))));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(AddressSet storage set, uint256 index) internal view returns (address) {\n return address(uint160(uint256(_at(set._inner, index))));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(AddressSet storage set) internal view returns (address[] memory) {\n bytes32[] memory store = _values(set._inner);\n address[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\n }\n\n // UintSet\n\n struct UintSet {\n Set _inner;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n *\n * Returns true if the value was added to the set, that is if it was not\n * already present.\n */\n function add(UintSet storage set, uint256 value) internal returns (bool) {\n return _add(set._inner, bytes32(value));\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n *\n * Returns true if the value was removed from the set, that is if it was\n * present.\n */\n function remove(UintSet storage set, uint256 value) internal returns (bool) {\n return _remove(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n return _contains(set._inner, bytes32(value));\n }\n\n /**\n * @dev Returns the number of values in the set. O(1).\n */\n function length(UintSet storage set) internal view returns (uint256) {\n return _length(set._inner);\n }\n\n /**\n * @dev Returns the value stored at position `index` in the set. O(1).\n *\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n return uint256(_at(set._inner, index));\n }\n\n /**\n * @dev Return the entire set in an array\n *\n * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed\n * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that\n * this function has an unbounded cost, and using it as part of a state-changing function may render the function\n * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.\n */\n function values(UintSet storage set) internal view returns (uint256[] memory) {\n bytes32[] memory store = _values(set._inner);\n uint256[] memory result;\n\n /// @solidity memory-safe-assembly\n assembly {\n result := store\n }\n\n return result;\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/TradeManager.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.16;\n\nimport { Initializable } from \"@openzeppelin/contracts/proxy/utils/Initializable.sol\";\nimport { EnumerableSet } from \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\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 { LinkTokenInterface } from \"@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol\";\nimport { IKeeperRegistrar, RegistrationParams } from \"src/interfaces/chainlink/IKeeperRegistrar.sol\";\nimport { LimitOrderRegistry } from \"src/LimitOrderRegistry.sol\";\nimport { UniswapV3Pool } from \"src/interfaces/uniswapV3/UniswapV3Pool.sol\";\n\n/**\n * @title Trade Manager\n * @notice Automates claiming limit orders for the LimitOrderRegistry.\n * @author crispymangoes\n * @dev Future improvements.\n * - could add logic into the LOR that checks if the caller is a users TradeManager, and if so that allows the caller to\n * create/edit orders on behalf of the user.\n * - add some bool that dictates where assets go, like on claim should assets be returned here, or to the owner\n * - Could allow users to funds their upkeep through this contract, which would interact with pegswap if needed.\n */\ncontract TradeManager is Initializable, AutomationCompatibleInterface, Owned {\n using SafeTransferLib for ERC20;\n using EnumerableSet for EnumerableSet.UintSet;\n\n /*//////////////////////////////////////////////////////////////\n STRUCTS\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Stores information used to claim orders in `performUpkeep`.\n * @param batchId The order batch id to claim\n * @param fee The Native fee required to claim the order\n */\n struct ClaimInfo {\n uint128 batchId;\n uint128 fee;\n }\n\n /*//////////////////////////////////////////////////////////////\n GLOBAL STATE\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Set of batch IDs that the owner currently has orders in.\n */\n EnumerableSet.UintSet private ownerOrders;\n\n /**\n * @notice The limit order registry contract this trade manager interacts with.\n */\n LimitOrderRegistry public limitOrderRegistry;\n\n /**\n * @notice The gas limit used when the Trade Managers upkeep is created.\n */\n uint32 public constant UPKEEP_GAS_LIMIT = 500_000;\n\n /**\n * @notice The max amount of claims that can happen in a single upkeep.\n */\n uint256 public constant MAX_CLAIMS = 10;\n\n /**\n * @notice Allows owner to specify whether they want claimed tokens to be left\n * in the TradeManager, or sent to their address.\n * -true send tokens to their address\n * -false leave tokens in the trade manager\n */\n bool public claimToOwner;\n\n constructor() Owned(address(0)) {}\n\n /*//////////////////////////////////////////////////////////////\n INITIALIZE LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Initialize function to setup this contract.\n * @param user The owner of this contract\n * @param _limitOrderRegistry The limit order registry this contract interacts with\n * @param LINK The Chainlink token needed to create an upkeep\n * @param registrar The Chainlink Automation Registrar contract\n * @param initialUpkeepFunds Amount of link to fund the upkeep with\n */\n function initialize(\n address user,\n LimitOrderRegistry _limitOrderRegistry,\n LinkTokenInterface LINK,\n IKeeperRegistrar registrar,\n uint256 initialUpkeepFunds\n ) external initializer {\n owner = user;\n limitOrderRegistry = _limitOrderRegistry;\n\n // Create a new upkeep.\n if (initialUpkeepFunds > 0) {\n ERC20(address(LINK)).safeTransferFrom(msg.sender, address(this), initialUpkeepFunds);\n ERC20(address(LINK)).safeApprove(address(registrar), initialUpkeepFunds);\n RegistrationParams memory params = RegistrationParams({\n name: \"Trade Manager\",\n encryptedEmail: abi.encode(0),\n upkeepContract: address(this),\n gasLimit: UPKEEP_GAS_LIMIT,\n adminAddress: user,\n checkData: abi.encode(0),\n offchainConfig: abi.encode(0),\n amount: uint96(initialUpkeepFunds)\n });\n registrar.registerUpkeep(params);\n }\n }\n\n /*//////////////////////////////////////////////////////////////\n OWNER LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Allows owner to adjust `claimToOwner`.\n */\n function setClaimToOwner(bool state) external onlyOwner {\n claimToOwner = state;\n }\n\n /**\n * @notice See `LimitOrderRegistry.sol:newOrder`.\n */\n function newOrder(\n UniswapV3Pool pool,\n ERC20 assetIn,\n int24 targetTick,\n uint128 amount,\n bool direction,\n uint256 startingNode\n ) external onlyOwner {\n uint256 managerBalance = assetIn.balanceOf(address(this));\n // If manager lacks funds, transfer delta into manager.\n if (managerBalance < amount) assetIn.safeTransferFrom(msg.sender, address(this), amount - managerBalance);\n\n assetIn.safeApprove(address(limitOrderRegistry), amount);\n uint128 batchId = limitOrderRegistry.newOrder(pool, targetTick, amount, direction, startingNode);\n ownerOrders.add(batchId);\n }\n\n /**\n * @notice See `LimitOrderRegistry.sol:cancelOrder`.\n */\n function cancelOrder(UniswapV3Pool pool, int24 targetTick, bool direction) external onlyOwner {\n (uint128 amount0, uint128 amount1, uint128 batchId) = limitOrderRegistry.cancelOrder(\n pool,\n targetTick,\n direction\n );\n if (amount0 > 0) ERC20(pool.token0()).safeTransfer(owner, amount0);\n if (amount1 > 0) ERC20(pool.token1()).safeTransfer(owner, amount1);\n\n ownerOrders.remove(batchId);\n }\n\n /**\n * @notice See `LimitOrderRegistry.sol:claimOrder`.\n */\n function claimOrder(uint128 batchId) external onlyOwner {\n uint256 value = limitOrderRegistry.getFeePerUser(batchId);\n limitOrderRegistry.claimOrder{ value: value }(batchId, address(this));\n\n ownerOrders.remove(batchId);\n }\n\n /**\n @notice Allows owner to withdraw Native asset from this contract.\n */\n function withdrawNative(uint256 amount) external onlyOwner {\n payable(owner).transfer(amount);\n }\n\n /**\n * @notice Allows owner to withdraw any ERC20 from this contract.\n */\n function withdrawERC20(ERC20 token, uint256 amount) external onlyOwner {\n token.safeTransfer(owner, amount);\n }\n\n receive() external payable {}\n\n /*//////////////////////////////////////////////////////////////\n CHAINLINK AUTOMATION LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Iterates through `ownerOrders` and stops early if total fee is greater than this contract native balance, or if max claims is met.\n */\n function checkUpkeep(bytes calldata) external view returns (bool upkeepNeeded, bytes memory performData) {\n uint256 nativeBalance = address(this).balance;\n // Iterate through owner orders, and build a claim array.\n\n uint256 count = ownerOrders.length();\n ClaimInfo[MAX_CLAIMS] memory claimInfo;\n uint256 claimCount;\n for (uint256 i; i < count; ++i) {\n uint128 batchId = uint128(ownerOrders.at(i));\n // Current order is not fulfilled.\n if (!limitOrderRegistry.isOrderReadyForClaim(batchId)) continue;\n uint128 fee = limitOrderRegistry.getFeePerUser(batchId);\n // Break if manager does not have enough native to pay for claim.\n if (fee > nativeBalance) break;\n // Subtract fee from balance.\n nativeBalance -= fee;\n claimInfo[claimCount].batchId = batchId;\n claimInfo[claimCount].fee = fee;\n claimCount++;\n // Break if max claims is reached.\n if (claimCount == MAX_CLAIMS) break;\n }\n\n if (claimCount > 0) {\n upkeepNeeded = true;\n performData = abi.encode(claimInfo);\n }\n // else nothing to do.\n }\n\n /**\n * @notice Accepts array of ClaimInfo.\n * @dev Passing in incorrect fee values will at worst cost the caller excess gas.\n * If fee is too large, excess is returned, or LimitOrderRegistry reverts when it tries to transfer Wrapped Native.\n * If fee is too small LimitOrderRegistry reverts when it tries to transfer Wrapped Native.\n */\n function performUpkeep(bytes calldata performData) external {\n // Accept claim array and claim all orders\n ClaimInfo[MAX_CLAIMS] memory claimInfo = abi.decode(performData, (ClaimInfo[10]));\n for (uint256 i; i < 10; ++i) {\n if (limitOrderRegistry.isOrderReadyForClaim(claimInfo[i].batchId)) {\n (ERC20 asset, uint256 assets) = limitOrderRegistry.claimOrder{ value: claimInfo[i].fee }(\n claimInfo[i].batchId,\n address(this)\n );\n ownerOrders.remove(claimInfo[i].batchId);\n if (claimToOwner) asset.safeTransfer(owner, assets);\n }\n }\n }\n\n function getOwnerBatchIds() external view returns (uint256[] memory ids) {\n ids = new uint256[](ownerOrders.length());\n for (uint256 i; i < ids.length; ++i) ids[i] = ownerOrders.at(i);\n }\n}\n"},"src/TradeManagerFactory.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 { LinkTokenInterface } from \"@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol\";\nimport { IKeeperRegistrar } from \"src/interfaces/chainlink/IKeeperRegistrar.sol\";\nimport { LimitOrderRegistry } from \"src/LimitOrderRegistry.sol\";\nimport { TradeManager } from \"src/TradeManager.sol\";\nimport { Clones } from \"@openzeppelin/contracts/proxy/Clones.sol\";\n\n/**\n * @title Trade Manager Factory\n * @notice Factory to deploy Trade Managers using Open Zeppelin Clones.\n * @author crispymangoes\n */\ncontract TradeManagerFactory {\n using SafeTransferLib for ERC20;\n using Clones for address;\n\n /*//////////////////////////////////////////////////////////////\n EVENTS\n //////////////////////////////////////////////////////////////*/\n\n event ManagerCreated(address manager);\n\n /*//////////////////////////////////////////////////////////////\n IMMUTABLES\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Trade Manager Implementation contract.\n */\n address public immutable implementation;\n\n constructor(address _implementation) {\n implementation = _implementation;\n }\n\n /*//////////////////////////////////////////////////////////////\n MANAGER CREATION LOGIC\n //////////////////////////////////////////////////////////////*/\n\n /**\n * @notice Allows caller to create a new trade manager for themselves.\n * @dev Requires caller has approved this contract to spend their LINK.\n */\n function createTradeManager(\n LimitOrderRegistry _limitOrderRegistry,\n LinkTokenInterface LINK,\n IKeeperRegistrar registrar,\n uint256 initialUpkeepFunds\n ) external returns (TradeManager manager) {\n address payable clone = payable(implementation.clone());\n if (initialUpkeepFunds > 0) {\n ERC20(address(LINK)).safeTransferFrom(msg.sender, address(this), initialUpkeepFunds);\n ERC20(address(LINK)).safeApprove(clone, initialUpkeepFunds);\n }\n manager = TradeManager(clone);\n manager.initialize(msg.sender, _limitOrderRegistry, LINK, registrar, initialUpkeepFunds);\n emit ManagerCreated(address(manager));\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":{}}}