[
  {
    "path": ".github/workflows/lint-from-fork.yml",
    "content": "name: Lint On PR\n\non:\n  pull_request_target:\n\njobs:\n  run-linters:\n    name: Run linters\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Check out Git repository\n        uses: actions/checkout@v2\n\n      - name: Set up node\n        uses: actions/setup-node@v2\n        with:\n          node-version: 16\n          registry-url: https://registry.npmjs.org\n\n      - name: Install dependencies\n        run: yarn install --frozen-lockfile\n\n      - name: Run linters\n        uses: wearerequired/lint-action@v1\n        with:\n          github_token: ${{ secrets.github_token }}\n          prettier: true\n          prettier_extensions: 'js,json,md,ts,yaml,yml'\n"
  },
  {
    "path": ".github/workflows/unit-tests-from-fork.yml",
    "content": "name: Unit Tests On PR\n\non:\n  pull_request_target:\n\njobs:\n  approve:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Approve\n        run: echo For security reasons, all pull requests need to be approved first before running any automated CI.\n\n  tests:\n    name: Unit tests\n\n    runs-on: ubuntu-latest\n\n    needs: [approve]\n\n    environment: test-env\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}\n          fetch-depth: 2\n\n      - name: Setup node\n        uses: actions/setup-node@v2\n        with:\n          node-version: 16\n          registry-url: https://registry.npmjs.org\n\n      - name: Install dependencies\n        run: yarn install --frozen-lockfile\n\n      - name: Generate typechain files from abi\n        run: yarn generate-types\n\n      - name: Run tests\n        env:\n          MAINNET_PROVIDER_URL: ${{ secrets.MAINNET_PROVIDER_URL }}\n        run: yarn test\n"
  },
  {
    "path": ".gitignore",
    "content": "# truffle build folder\nbuild\n\n# saddle build folder\n.build/\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\ncoverage.json\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n# deploy info file\ndeploy.txt\n\n# slither requires a hardcoded absolute path for open-zeppelin libraries, so-called \"remapping\"\nslither.config.json\n\n# local vscode config\n.vscode/\n\nvaults.json\n\n# --standard-json\nsolcinputjson.sh\noutput.json\n\n# mocha test report\nmochawesome-report/\n\n#prettier ignore\n.prettierignore\n\n#hardhat\nartifacts/\ncache/\ntypechain/"
  },
  {
    "path": ".prettierrc.json",
    "content": "{}\n"
  },
  {
    "path": "LICENSE",
    "content": "Business Source License 1.1\n\nLicense text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.\n\"Business Source License\" is a trademark of MariaDB Corporation Ab.\n\n---\n\nParameters\n\nLicensor: Uniswap Labs\n\nLicensed Work: Uniswap V3 Core\nThe Licensed Work is (c) 2021 Uniswap Labs\n\nAdditional Use Grant: Any uses listed and defined at\nv3-core-license-grants.uniswap.eth\n\nChange Date: The earlier of 2023-04-01 or a date specified at\nv3-core-license-date.uniswap.eth\n\nChange License: GNU General Public License v2.0 or later\n\n---\n\nTerms\n\nThe Licensor hereby grants you the right to copy, modify, create derivative\nworks, redistribute, and make non-production use of the Licensed Work. The\nLicensor may make an Additional Use Grant, above, permitting limited\nproduction use.\n\nEffective on the Change Date, or the fourth anniversary of the first publicly\navailable distribution of a specific version of the Licensed Work under this\nLicense, whichever comes first, the Licensor hereby grants you rights under\nthe terms of the Change License, and the rights granted in the paragraph\nabove terminate.\n\nIf your use of the Licensed Work does not comply with the requirements\ncurrently in effect as described in this License, you must purchase a\ncommercial license from the Licensor, its affiliated entities, or authorized\nresellers, or you must refrain from using the Licensed Work.\n\nAll copies of the original and modified Licensed Work, and derivative works\nof the Licensed Work, are subject to this License. This License applies\nseparately for each version of the Licensed Work and the Change Date may vary\nfor each version of the Licensed Work released by Licensor.\n\nYou must conspicuously display this License on each original or modified copy\nof the Licensed Work. If you receive the Licensed Work in original or\nmodified form from a third party, the terms and conditions set forth in this\nLicense apply to your use of that work.\n\nAny use of the Licensed Work in violation of this License will automatically\nterminate your rights under this License for the current and all other\nversions of the Licensed Work.\n\nThis License does not grant you any right in any trademark or logo of\nLicensor or its affiliates (provided that you may use a trademark or logo of\nLicensor as expressly required by this License).\n\nTO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON\nAN \"AS IS\" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,\nEXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND\nTITLE.\n\nMariaDB hereby grants you permission to use this License’s text to license\nyour works, and to refer to it using the trademark \"Business Source License\",\nas long as you comply with the Covenants of Licensor below.\n\n---\n\nCovenants of Licensor\n\nIn consideration of the right to use this License’s text and the \"Business\nSource License\" name and trademark, Licensor covenants to MariaDB, and to all\nother recipients of the licensed work to be provided by Licensor:\n\n1. To specify as the Change License the GPL Version 2.0 or any later version,\n   or a license that is compatible with GPL Version 2.0 or a later version,\n   where \"compatible\" means that software provided under the Change License can\n   be included in a program with software provided under GPL Version 2.0 or a\n   later version. Licensor may specify additional Change Licenses without\n   limitation.\n\n2. To either: (a) specify an additional grant of rights to use that does not\n   impose any additional restriction on the right granted in this License, as\n   the Additional Use Grant; or (b) insert the text \"None\".\n\n3. To specify a Change Date.\n\n4. Not to modify this License in any other way.\n\n---\n\nNotice\n\nThe Business Source License (this document, or the \"License\") is not an Open\nSource license. However, the Licensed Work will eventually be made available\nunder an Open Source License, as stated in this License.\n"
  },
  {
    "path": "README.md",
    "content": "[![Twitter URL](https://img.shields.io/twitter/url.svg?label=Follow%20%40BellaProtocol&style=social&url=https%3A%2F%2Ftwitter.com%2FBellaProtocol)](https://twitter.com/BellaProtocol)\n[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/8ctd5geS8t)\n\n# the \"_Tuner_\", a programmable, transaction-based Uniswap V3 Simulator with 100% Precision\n\n> Before an orchestra, every musical instrument has to be _in-tune_, to ensure an outstanding performance.\n>\n> Before running a strategy, every parameter has to be _fine tuned_, to maximize the performance.\n\n#### _Tuner_ is a programmatic Uniswap V3 simulator that allows strategy backtesting on a transaction-to-transaction basis with arbitrary or historical data without the EVM, it runs independently yet completely retains the exact smart-contract behavior of the intricate design and implementation of Uniswap V3.\n\n## Documentation\n\n- [Getting Started](https://docs.bella.fi/getting-started)\n- [Configuration](https://docs.bella.fi/configuration)\n- [Guides](https://docs.bella.fi/guides)\n- Developer Articles(stay tuned)\n\n#### _Tuner_ is fundamentally a state machine, it can:\n\n> **Completely replicate the tick-level calculation**\n\n- this means your strategy will run through the Uniswap V3 implementation logic instead of just the high-level mathematic model.\n\n> **Maintain the identical tick-level precision of prices, fees, and positions of Uniswap V3**\n\n- this means the result of your backtesting is true to the real performance with the minimum margin of deviations.\n\n> **Run fast**\n\n- the EVM is slow, the historical dataset is huge, the Ganache cannot do the job, so use _Tuner_.\n\n> **Fast-forward and rewind transactions**\n\n- this means you can easily repeat a small portion of your test with a different set of parameters without the need to start over.\n\n> **Take or recover from a snapshot(state)**\n\n- this means you can run continuous regression test as your strategies constantly evolves.\n\n> **Branch out and runs in parallel**\n\n- this means you can run multiple back-tests each with a different set of parameters at the same time and compare the performance.\n\n> **Persist historical data and strategy execution records in a SQLite database**\n\n- this means the strategists can do advanced statistical analysis both in real-time and after the testing.\n"
  },
  {
    "path": "SUMMARY.md",
    "content": "# Table of contents\n\n## Getting Started\n\n- [Overview](README.md)\n\n- [How \"Tuner\" Library Works?](docs/how-tuner-library-works.md)\n\n- [Installing \"Tuner\"](docs/installing-tuner.md)\n\n- [Quick Start](docs/quick-start.md)\n\n## Configuration\n\n- [Configuration](docs/configuration.md)\n\n## Guides\n\n- (Basic)For anyone who is interested in the Uniswap v3 model\n\n  - [Building a client instance](docs/building-a-client-instance.md)\n  - [About Core Pool Config](docs/about-core-pool-config.md)\n  - [Getting a Core Pool instance](docs/getting-a-core-pool-instance.md)\n  - [Interacting with Core Pool](docs/interacting-with-core-pool.md)\n\n- (Typical)For a quant developer who works on a real pool on mainnet\n\n  - [Fetching all the data of a certain pool from Ethereum](docs/fetching-all-the-data-of-a-certain-pool-from-ethereum.md)\n  - [Getting a pool instance with the data fetched](docs/getting-a-pool-instance-with-the-data-fetched.md)\n  - [Loading and streaming events into a pool](docs/loading-and-streaming-events-into-a-pool.md)\n\n- (Advanced)For a better user experience as a state machine\n\n  - [PoolState & Transition](docs/pool-state-and-transition.md)\n  - [Post-processor](docs/post-processor.md)\n  - [Forking & Retracing](docs/forking-and-retracing.md)\n  - [Persisting & Recovering](docs/persisting-and-recovering.md)\n  - [SimulatorRoadmapManager](docs/simulator-roadmap-manager.md)\n\n## Performance\n\n- [Performance](docs/performance.md)\n\n## Examples\n\n- [Uniswap-v3-Events-Downloader](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Events-Downloader)\n\n- [Uniswap-v3-Strategy-Backtest](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Strategy-Backtest)\n\n- Uniswap-v3-Risk-Analysis(will be available soon)\n\n## Contributing\n\n- [Contributing](docs/contributing.md)\n"
  },
  {
    "path": "abi/OHM.json",
    "content": "[\n  { \"inputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"constructor\" },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"owner\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"spender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"value\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Approval\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"previousOwner\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"newOwner\",\n        \"type\": \"address\"\n      }\n    ],\n    \"name\": \"OwnershipTransferred\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"previousTWAPEpochPeriod\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"newTWAPEpochPeriod\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"TWAPEpochChanged\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"previousTWAPOracle\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"newTWAPOracle\",\n        \"type\": \"address\"\n      }\n    ],\n    \"name\": \"TWAPOracleChanged\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"newTWAPSource\",\n        \"type\": \"address\"\n      }\n    ],\n    \"name\": \"TWAPSourceAdded\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"removedTWAPSource\",\n        \"type\": \"address\"\n      }\n    ],\n    \"name\": \"TWAPSourceRemoved\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"from\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"to\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"value\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Transfer\",\n    \"type\": \"event\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"DOMAIN_SEPARATOR\",\n    \"outputs\": [{ \"internalType\": \"bytes32\", \"name\": \"\", \"type\": \"bytes32\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"PERMIT_TYPEHASH\",\n    \"outputs\": [{ \"internalType\": \"bytes32\", \"name\": \"\", \"type\": \"bytes32\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"account_\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount_\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"_burnFrom\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"newTWAPSourceDexPool_\",\n        \"type\": \"address\"\n      }\n    ],\n    \"name\": \"addTWAPSource\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"owner\", \"type\": \"address\" },\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" }\n    ],\n    \"name\": \"allowance\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"approve\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"account\", \"type\": \"address\" }\n    ],\n    \"name\": \"balanceOf\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"burn\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"account_\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount_\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"burnFrom\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"newTWAPEpochPeriod_\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"changeTWAPEpochPeriod\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"newTWAPOracle_\", \"type\": \"address\" }\n    ],\n    \"name\": \"changeTWAPOracle\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"decimals\",\n    \"outputs\": [{ \"internalType\": \"uint8\", \"name\": \"\", \"type\": \"uint8\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"subtractedValue\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"decreaseAllowance\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"addedValue\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"increaseAllowance\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"account_\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount_\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"mint\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"name\",\n    \"outputs\": [{ \"internalType\": \"string\", \"name\": \"\", \"type\": \"string\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"owner\", \"type\": \"address\" }\n    ],\n    \"name\": \"nonces\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"owner\",\n    \"outputs\": [{ \"internalType\": \"address\", \"name\": \"\", \"type\": \"address\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"owner\", \"type\": \"address\" },\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"deadline\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint8\", \"name\": \"v\", \"type\": \"uint8\" },\n      { \"internalType\": \"bytes32\", \"name\": \"r\", \"type\": \"bytes32\" },\n      { \"internalType\": \"bytes32\", \"name\": \"s\", \"type\": \"bytes32\" }\n    ],\n    \"name\": \"permit\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"twapSourceToRemove_\",\n        \"type\": \"address\"\n      }\n    ],\n    \"name\": \"removeTWAPSource\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"renounceOwnership\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"vault_\", \"type\": \"address\" }\n    ],\n    \"name\": \"setVault\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"symbol\",\n    \"outputs\": [{ \"internalType\": \"string\", \"name\": \"\", \"type\": \"string\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"totalSupply\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"recipient\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"transfer\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"sender\", \"type\": \"address\" },\n      { \"internalType\": \"address\", \"name\": \"recipient\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"transferFrom\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"newOwner_\", \"type\": \"address\" }\n    ],\n    \"name\": \"transferOwnership\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"twapEpochPeriod\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"twapOracle\",\n    \"outputs\": [\n      { \"internalType\": \"contract ITWAPOracle\", \"name\": \"\", \"type\": \"address\" }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"vault\",\n    \"outputs\": [{ \"internalType\": \"address\", \"name\": \"\", \"type\": \"address\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  }\n]\n"
  },
  {
    "path": "abi/UniswapV3Pool2.json",
    "content": "[\n  {\n    \"inputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"constructor\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"owner\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"int24\",\n        \"name\": \"tickLower\",\n        \"type\": \"int24\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"int24\",\n        \"name\": \"tickUpper\",\n        \"type\": \"int24\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint128\",\n        \"name\": \"amount\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Burn\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"owner\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"int24\",\n        \"name\": \"tickLower\",\n        \"type\": \"int24\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"int24\",\n        \"name\": \"tickUpper\",\n        \"type\": \"int24\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint128\",\n        \"name\": \"amount0\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint128\",\n        \"name\": \"amount1\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"name\": \"Collect\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"sender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint128\",\n        \"name\": \"amount0\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint128\",\n        \"name\": \"amount1\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"name\": \"CollectProtocol\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"sender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"paid0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"paid1\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Flash\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint16\",\n        \"name\": \"observationCardinalityNextOld\",\n        \"type\": \"uint16\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint16\",\n        \"name\": \"observationCardinalityNextNew\",\n        \"type\": \"uint16\"\n      }\n    ],\n    \"name\": \"IncreaseObservationCardinalityNext\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint160\",\n        \"name\": \"sqrtPriceX96\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"int24\",\n        \"name\": \"tick\",\n        \"type\": \"int24\"\n      }\n    ],\n    \"name\": \"Initialize\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": false,\n        \"internalType\": \"address\",\n        \"name\": \"sender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"owner\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"int24\",\n        \"name\": \"tickLower\",\n        \"type\": \"int24\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"int24\",\n        \"name\": \"tickUpper\",\n        \"type\": \"int24\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint128\",\n        \"name\": \"amount\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Mint\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint8\",\n        \"name\": \"feeProtocol0Old\",\n        \"type\": \"uint8\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint8\",\n        \"name\": \"feeProtocol1Old\",\n        \"type\": \"uint8\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint8\",\n        \"name\": \"feeProtocol0New\",\n        \"type\": \"uint8\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint8\",\n        \"name\": \"feeProtocol1New\",\n        \"type\": \"uint8\"\n      }\n    ],\n    \"name\": \"SetFeeProtocol\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"sender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"int256\",\n        \"name\": \"amount0\",\n        \"type\": \"int256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"int256\",\n        \"name\": \"amount1\",\n        \"type\": \"int256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint160\",\n        \"name\": \"sqrtPriceX96\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint128\",\n        \"name\": \"liquidity\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"int24\",\n        \"name\": \"tick\",\n        \"type\": \"int24\"\n      }\n    ],\n    \"name\": \"Swap\",\n    \"type\": \"event\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickLower\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickUpper\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"name\": \"burn\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickLower\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickUpper\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount0Requested\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount1Requested\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"name\": \"collect\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount0\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount1\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount0Requested\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount1Requested\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"name\": \"collectProtocol\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount0\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount1\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"factory\",\n    \"outputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"\",\n        \"type\": \"address\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"fee\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint24\",\n        \"name\": \"\",\n        \"type\": \"uint24\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"feeGrowthGlobal0X128\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"feeGrowthGlobal1X128\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"bytes\",\n        \"name\": \"data\",\n        \"type\": \"bytes\"\n      }\n    ],\n    \"name\": \"flash\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"uint16\",\n        \"name\": \"observationCardinalityNext\",\n        \"type\": \"uint16\"\n      }\n    ],\n    \"name\": \"increaseObservationCardinalityNext\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"uint160\",\n        \"name\": \"sqrtPriceX96\",\n        \"type\": \"uint160\"\n      }\n    ],\n    \"name\": \"initialize\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"liquidity\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"maxLiquidityPerTick\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickLower\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickUpper\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"amount\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"bytes\",\n        \"name\": \"data\",\n        \"type\": \"bytes\"\n      }\n    ],\n    \"name\": \"mint\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"observations\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint32\",\n        \"name\": \"blockTimestamp\",\n        \"type\": \"uint32\"\n      },\n      {\n        \"internalType\": \"int56\",\n        \"name\": \"tickCumulative\",\n        \"type\": \"int56\"\n      },\n      {\n        \"internalType\": \"uint160\",\n        \"name\": \"secondsPerLiquidityCumulativeX128\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"internalType\": \"bool\",\n        \"name\": \"initialized\",\n        \"type\": \"bool\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"uint32[]\",\n        \"name\": \"secondsAgos\",\n        \"type\": \"uint32[]\"\n      }\n    ],\n    \"name\": \"observe\",\n    \"outputs\": [\n      {\n        \"internalType\": \"int56[]\",\n        \"name\": \"tickCumulatives\",\n        \"type\": \"int56[]\"\n      },\n      {\n        \"internalType\": \"uint160[]\",\n        \"name\": \"secondsPerLiquidityCumulativeX128s\",\n        \"type\": \"uint160[]\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"bytes32\",\n        \"name\": \"\",\n        \"type\": \"bytes32\"\n      }\n    ],\n    \"name\": \"positions\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"liquidity\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"feeGrowthInside0LastX128\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"feeGrowthInside1LastX128\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"tokensOwed0\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"tokensOwed1\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"protocolFees\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"token0\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"token1\",\n        \"type\": \"uint128\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"uint8\",\n        \"name\": \"feeProtocol0\",\n        \"type\": \"uint8\"\n      },\n      {\n        \"internalType\": \"uint8\",\n        \"name\": \"feeProtocol1\",\n        \"type\": \"uint8\"\n      }\n    ],\n    \"name\": \"setFeeProtocol\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tick\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"uint160\",\n        \"name\": \"sqrtPriceX96\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"_liquidity\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"_feeGrowthGlobal0X128\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"_feeGrowthGlobal1X128\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"setState\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"int16\",\n        \"name\": \"wordPos\",\n        \"type\": \"int16\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"wordVal\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"setTickBitmap\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tick\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"liquidityGross\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"int128\",\n        \"name\": \"liquidityNet\",\n        \"type\": \"int128\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"feeGrowthOutside0X128\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"feeGrowthOutside1X128\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"bool\",\n        \"name\": \"initialized\",\n        \"type\": \"bool\"\n      }\n    ],\n    \"name\": \"setTickInfo\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"slot0\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint160\",\n        \"name\": \"sqrtPriceX96\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tick\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"uint16\",\n        \"name\": \"observationIndex\",\n        \"type\": \"uint16\"\n      },\n      {\n        \"internalType\": \"uint16\",\n        \"name\": \"observationCardinality\",\n        \"type\": \"uint16\"\n      },\n      {\n        \"internalType\": \"uint16\",\n        \"name\": \"observationCardinalityNext\",\n        \"type\": \"uint16\"\n      },\n      {\n        \"internalType\": \"uint8\",\n        \"name\": \"feeProtocol\",\n        \"type\": \"uint8\"\n      },\n      {\n        \"internalType\": \"bool\",\n        \"name\": \"unlocked\",\n        \"type\": \"bool\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickLower\",\n        \"type\": \"int24\"\n      },\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"tickUpper\",\n        \"type\": \"int24\"\n      }\n    ],\n    \"name\": \"snapshotCumulativesInside\",\n    \"outputs\": [\n      {\n        \"internalType\": \"int56\",\n        \"name\": \"tickCumulativeInside\",\n        \"type\": \"int56\"\n      },\n      {\n        \"internalType\": \"uint160\",\n        \"name\": \"secondsPerLiquidityInsideX128\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"internalType\": \"uint32\",\n        \"name\": \"secondsInside\",\n        \"type\": \"uint32\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"recipient\",\n        \"type\": \"address\"\n      },\n      {\n        \"internalType\": \"bool\",\n        \"name\": \"zeroForOne\",\n        \"type\": \"bool\"\n      },\n      {\n        \"internalType\": \"int256\",\n        \"name\": \"amountSpecified\",\n        \"type\": \"int256\"\n      },\n      {\n        \"internalType\": \"uint160\",\n        \"name\": \"sqrtPriceLimitX96\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"internalType\": \"bytes\",\n        \"name\": \"data\",\n        \"type\": \"bytes\"\n      }\n    ],\n    \"name\": \"swap\",\n    \"outputs\": [\n      {\n        \"internalType\": \"int256\",\n        \"name\": \"amount0\",\n        \"type\": \"int256\"\n      },\n      {\n        \"internalType\": \"int256\",\n        \"name\": \"amount1\",\n        \"type\": \"int256\"\n      }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"int16\",\n        \"name\": \"\",\n        \"type\": \"int16\"\n      }\n    ],\n    \"name\": \"tickBitmap\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"tickSpacing\",\n    \"outputs\": [\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"\",\n        \"type\": \"int24\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"int24\",\n        \"name\": \"\",\n        \"type\": \"int24\"\n      }\n    ],\n    \"name\": \"ticks\",\n    \"outputs\": [\n      {\n        \"internalType\": \"uint128\",\n        \"name\": \"liquidityGross\",\n        \"type\": \"uint128\"\n      },\n      {\n        \"internalType\": \"int128\",\n        \"name\": \"liquidityNet\",\n        \"type\": \"int128\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"feeGrowthOutside0X128\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"feeGrowthOutside1X128\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"internalType\": \"int56\",\n        \"name\": \"tickCumulativeOutside\",\n        \"type\": \"int56\"\n      },\n      {\n        \"internalType\": \"uint160\",\n        \"name\": \"secondsPerLiquidityOutsideX128\",\n        \"type\": \"uint160\"\n      },\n      {\n        \"internalType\": \"uint32\",\n        \"name\": \"secondsOutside\",\n        \"type\": \"uint32\"\n      },\n      {\n        \"internalType\": \"bool\",\n        \"name\": \"initialized\",\n        \"type\": \"bool\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"token0\",\n    \"outputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"\",\n        \"type\": \"address\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"token1\",\n    \"outputs\": [\n      {\n        \"internalType\": \"address\",\n        \"name\": \"\",\n        \"type\": \"address\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  }\n]\n"
  },
  {
    "path": "abi/Visor.json",
    "content": "[\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"_pool\", \"type\": \"address\" },\n      { \"internalType\": \"address\", \"name\": \"_owner\", \"type\": \"address\" },\n      { \"internalType\": \"string\", \"name\": \"name\", \"type\": \"string\" },\n      { \"internalType\": \"string\", \"name\": \"symbol\", \"type\": \"string\" }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"constructor\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"owner\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"spender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"value\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Approval\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"sender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"to\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"shares\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Deposit\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": false,\n        \"internalType\": \"int24\",\n        \"name\": \"tick\",\n        \"type\": \"int24\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"totalAmount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"totalAmount1\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"feeAmount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"feeAmount1\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"totalSupply\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Rebalance\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"from\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"to\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"value\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Transfer\",\n    \"type\": \"event\"\n  },\n  {\n    \"anonymous\": false,\n    \"inputs\": [\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"sender\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": true,\n        \"internalType\": \"address\",\n        \"name\": \"to\",\n        \"type\": \"address\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"shares\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount0\",\n        \"type\": \"uint256\"\n      },\n      {\n        \"indexed\": false,\n        \"internalType\": \"uint256\",\n        \"name\": \"amount1\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"Withdraw\",\n    \"type\": \"event\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"PRECISION\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"owner\", \"type\": \"address\" },\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" }\n    ],\n    \"name\": \"allowance\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address[]\", \"name\": \"listed\", \"type\": \"address[]\" }\n    ],\n    \"name\": \"appendList\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"approve\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"account\", \"type\": \"address\" }\n    ],\n    \"name\": \"balanceOf\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"baseLower\",\n    \"outputs\": [{ \"internalType\": \"int24\", \"name\": \"\", \"type\": \"int24\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"baseUpper\",\n    \"outputs\": [{ \"internalType\": \"int24\", \"name\": \"\", \"type\": \"int24\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"currentTick\",\n    \"outputs\": [{ \"internalType\": \"int24\", \"name\": \"tick\", \"type\": \"int24\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"decimals\",\n    \"outputs\": [{ \"internalType\": \"uint8\", \"name\": \"\", \"type\": \"uint8\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" },\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"subtractedValue\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"decreaseAllowance\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"deposit0\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"deposit1\", \"type\": \"uint256\" },\n      { \"internalType\": \"address\", \"name\": \"to\", \"type\": \"address\" }\n    ],\n    \"name\": \"deposit\",\n    \"outputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"shares\", \"type\": \"uint256\" }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"deposit0Max\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"deposit1Max\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"fee\",\n    \"outputs\": [{ \"internalType\": \"uint24\", \"name\": \"\", \"type\": \"uint24\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"getBasePosition\",\n    \"outputs\": [\n      { \"internalType\": \"uint128\", \"name\": \"liquidity\", \"type\": \"uint128\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount0\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount1\", \"type\": \"uint256\" }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"getLimitPosition\",\n    \"outputs\": [\n      { \"internalType\": \"uint128\", \"name\": \"liquidity\", \"type\": \"uint128\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount0\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount1\", \"type\": \"uint256\" }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"getTotalAmounts\",\n    \"outputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"total0\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"total1\", \"type\": \"uint256\" }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"spender\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"addedValue\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"increaseAllowance\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"limitLower\",\n    \"outputs\": [{ \"internalType\": \"int24\", \"name\": \"\", \"type\": \"int24\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"limitUpper\",\n    \"outputs\": [{ \"internalType\": \"int24\", \"name\": \"\", \"type\": \"int24\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [{ \"internalType\": \"address\", \"name\": \"\", \"type\": \"address\" }],\n    \"name\": \"list\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"maxTotalSupply\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"name\",\n    \"outputs\": [{ \"internalType\": \"string\", \"name\": \"\", \"type\": \"string\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"owner\",\n    \"outputs\": [{ \"internalType\": \"address\", \"name\": \"\", \"type\": \"address\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"pool\",\n    \"outputs\": [\n      {\n        \"internalType\": \"contract IUniswapV3Pool\",\n        \"name\": \"\",\n        \"type\": \"address\"\n      }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"int24\", \"name\": \"_baseLower\", \"type\": \"int24\" },\n      { \"internalType\": \"int24\", \"name\": \"_baseUpper\", \"type\": \"int24\" },\n      { \"internalType\": \"int24\", \"name\": \"_limitLower\", \"type\": \"int24\" },\n      { \"internalType\": \"int24\", \"name\": \"_limitUpper\", \"type\": \"int24\" },\n      { \"internalType\": \"address\", \"name\": \"feeRecipient\", \"type\": \"address\" },\n      { \"internalType\": \"int256\", \"name\": \"swapQuantity\", \"type\": \"int256\" }\n    ],\n    \"name\": \"rebalance\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"_deposit0Max\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"_deposit1Max\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"setDepositMax\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      {\n        \"internalType\": \"uint256\",\n        \"name\": \"_maxTotalSupply\",\n        \"type\": \"uint256\"\n      }\n    ],\n    \"name\": \"setMaxTotalSupply\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"symbol\",\n    \"outputs\": [{ \"internalType\": \"string\", \"name\": \"\", \"type\": \"string\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"tickSpacing\",\n    \"outputs\": [{ \"internalType\": \"int24\", \"name\": \"\", \"type\": \"int24\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"toggleWhitelist\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"token0\",\n    \"outputs\": [\n      { \"internalType\": \"contract IERC20\", \"name\": \"\", \"type\": \"address\" }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"token1\",\n    \"outputs\": [\n      { \"internalType\": \"contract IERC20\", \"name\": \"\", \"type\": \"address\" }\n    ],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"totalSupply\",\n    \"outputs\": [{ \"internalType\": \"uint256\", \"name\": \"\", \"type\": \"uint256\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"recipient\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"transfer\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"sender\", \"type\": \"address\" },\n      { \"internalType\": \"address\", \"name\": \"recipient\", \"type\": \"address\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount\", \"type\": \"uint256\" }\n    ],\n    \"name\": \"transferFrom\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"address\", \"name\": \"newOwner\", \"type\": \"address\" }\n    ],\n    \"name\": \"transferOwnership\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"amount0\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount1\", \"type\": \"uint256\" },\n      { \"internalType\": \"bytes\", \"name\": \"data\", \"type\": \"bytes\" }\n    ],\n    \"name\": \"uniswapV3MintCallback\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"int256\", \"name\": \"amount0Delta\", \"type\": \"int256\" },\n      { \"internalType\": \"int256\", \"name\": \"amount1Delta\", \"type\": \"int256\" },\n      { \"internalType\": \"bytes\", \"name\": \"data\", \"type\": \"bytes\" }\n    ],\n    \"name\": \"uniswapV3SwapCallback\",\n    \"outputs\": [],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [],\n    \"name\": \"whitelisted\",\n    \"outputs\": [{ \"internalType\": \"bool\", \"name\": \"\", \"type\": \"bool\" }],\n    \"stateMutability\": \"view\",\n    \"type\": \"function\"\n  },\n  {\n    \"inputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"shares\", \"type\": \"uint256\" },\n      { \"internalType\": \"address\", \"name\": \"to\", \"type\": \"address\" },\n      { \"internalType\": \"address\", \"name\": \"from\", \"type\": \"address\" }\n    ],\n    \"name\": \"withdraw\",\n    \"outputs\": [\n      { \"internalType\": \"uint256\", \"name\": \"amount0\", \"type\": \"uint256\" },\n      { \"internalType\": \"uint256\", \"name\": \"amount1\", \"type\": \"uint256\" }\n    ],\n    \"stateMutability\": \"nonpayable\",\n    \"type\": \"function\"\n  }\n]\n"
  },
  {
    "path": "docs/about-core-pool-config.md",
    "content": "### About Core Pool Config\n\nA `PoolConfig` is a group of key meta parameters of an Uniswap V3 Core Pool. In details, `TickSpacing`, `Token0Address`, `Token1Address`, and `FeeAmount`.\n\nIf you want to build a Core Pool from scratch to study something about the math model, you have to build it manually before build that pool. And it won't bother you if you want to get an instance from the state of some existed core pool on chain.\n\n```typescript\nlet configurableCorePool: IConfigurableCorePool =\n  clientInstance.initCorePoolFromConfig(\n    new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)\n  );\n```\n"
  },
  {
    "path": "docs/building-a-client-instance.md",
    "content": "### Building a client instance\n\nA `SimulatorClient` is the user entry point of the Tuner. It needs a `SimulationDataManager` to persist internal data supporting functionality of the simulator, while external data refer to state of some Uniswap v3 Core Pool on chain.\n\nWe provide an implementation of `SimulationDataManager` using SQLite, so it's recommended to provide a file path locally to save the internal data(in memory by default), then you can recover a pool from a snapshot. If you are familiar with the interface, it's ok to replace it with a customed implementation, e.g. some database connected remotely.\n\n```typescript\n// 1. Instantiate a SimulationDataManager\n// this is for handling the internal data (snapshots, roadmaps, etc.)\nlet simulationDataManager: SimulationDataManager =\n  await SQLiteSimulationDataManager.buildInstance(\n    \"Your file path to save the internal data\"\n  );\nlet clientInstance: SimulatorClient = new SimulatorClient(\n  simulationDataManager\n);\n```\n\nIt's recommended to close the client when you finish with Tuner.\n\n```typescript\nawait clientInstance.shutdown();\n```\n"
  },
  {
    "path": "docs/configuration.md",
    "content": "### Configuration\n\nWhen Tuner is run, it searches for the closest tuner.config.js file starting from the Current Working Directory. This file normally lives in the root of your project. The `RPCProviderUrl` need to be specified, and it's recommended to setup an environment variable:\n\n```javascript\nmodule.exports = {\n  RPCProviderUrl: process.env.MAINNET_PROVIDER_URL,\n};\n```\n"
  },
  {
    "path": "docs/contributing.md",
    "content": "### Contributing\n\nPlease create an issue on github repo for any questions.\n\nThanks and enjoy!\n"
  },
  {
    "path": "docs/fetching-all-the-data-of-a-certain-pool-from-ethereum.md",
    "content": "### Fetching all the data of a certain pool from Ethereum\n\nTuner syncs the full state of the Pool from the event logs on chain, aiming to reproduce 100% as things happened in the mainnet.\n\nFor a quant developer, Tuner offers an easy-to-use API to download/update and persist event logs, and then build the core pool instance for the user. According to transaction volume of wanted core pool, the first run will cost some time. See [Performance](performance.md).\n\nSpecifically, you can set block number or other key dates as the upper limit from the deployment of your specified core pool, as time range of event logs. The SimulatorClient will download data up to that block and save it to a SQLite database file locally.\n\nNote: The database for event logs(external data) and the one to support functionality of Tuner(internal data) are different SQLite database files.\n\n```typescript\nexport type EndBlockTypeWhenInit =\n  | number\n  | \"latest\"\n  | \"afterDeployment\"\n  | \"afterInitialization\";\n\nexport type EndBlockTypeWhenRecover =\n  | number\n  | \"latestOnChain\"\n  | \"latestDownloaded\"\n  | \"afterDeployment\"\n  | \"afterInitialization\";\n```\n\nFor downloading mainnet data, we had to pre-process the data because mainnet events of swaps only represent the results of the swap, we had to use a try-and-error logic to test out the actual inputs of that swap.\n\nThis means, there is more information added on top of the mainnet events, and this extra information (correct inputs of the swap that emit the event recorded on mainnet) is only added during the download process.\n\n`SimulatorClient` will handle with processes above and you just need to call `clientInstance.initCorePoolFromMainnet` to download event logs for a new pool or `clientInstance.recoverFromMainnetEventDBFile` to update event logs for an existed pool. They both give you an instance of `ConfigurableCorePool` finally.\n\nAn example of the whole process will be:\n\n```typescript\nimport {\n  EndBlockTypeWhenRecover,\n  EventDataSourceType,\n  SimulationDataManager,\n  SimulatorClient,\n  SQLiteSimulationDataManager,\n} from \"@bella-defintech/uniswap-v3-simulator\";\n\n// 1. Instantiate a SimulationDataManager\n// this is for handling the internal data (snapshots, roadmaps, etc.)\nlet simulationDataManager: SimulationDataManager =\n  await SQLiteSimulationDataManager.buildInstance(\n    \"Your file path to save the internal data\"\n  );\nlet clientInstance: SimulatorClient = new SimulatorClient(\n  simulationDataManager\n);\n\n// 2. Specify the core pool you want and time range of event logs to sync\nlet poolName = \"events-test\";\n\n// This would be the contract address of a certain Uniswap V3 Pool\nlet poolAddress = \"0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8\";\n\nlet endBlock: EndBlockTypeWhenRecover = 12374077;\n\n// This is the rpc provider url for ethers.js, you can customize it here or use the value in tuner.config.js\nlet RPCProviderUrl: string | undefined = \"Your customed RPCProviderUrl\";\n\n// You can specify data source of events here, and Uniswap v3 Subgraph as default is recommended rather than RPC for at least 75% time saving\n// Just a reminder, RPC endpoint is necessary for the simulator even if you choose to download events from Subgraph\nlet eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH;\n\n// 3(a). This method helps you:\n//    Download event data of a certain Uniswap V3 pool from mainnet\n//    Pre-process the data to figure out the inputs of swap events\n//    Finally get the core pool instance\nif (!exists(`${poolName}_${poolAddress}.db`)) {\n  let configurableCorePool: ConfigurableCorePool =\n    await clientInstance.initCorePoolFromMainnet(\n      poolName,\n      poolAddress,\n      endBlock,\n      RPCProviderUrl,\n      eventDataSourceType\n    );\n}\n\n// 3(b). This method helps you:\n//    Update and Pre-process event data of a certain Uniswap V3 pool from mainnet if necessary\n//    Build a simulated CorePool instance from the downloaded-and-pre-processed mainnet events\nlet configurableCorePool: ConfigurableCorePool =\n  await clientInstance.recoverFromMainnetEventDBFile(\n    `${poolName}_${poolAddress}.db`,\n    endBlock,\n    RPCProviderUrl,\n    eventDataSourceType\n  );\n\n// 4. It's recommended to close the client when you finish with Tuner\nawait clientInstance.shutdown();\n```\n\nNote: If a bad gateway error(usually due to the hosted service of subgraph) happens, just wait a few seconds and then give it a re-run. Tuner has taken error handling into consideration to make sure integrality and consistency of events.\n"
  },
  {
    "path": "docs/forking-and-retracing.md",
    "content": "### Forking & Retracing\n\nDuring the lifetime of a program, Tuner will record every step that a `ConfigurableCorePool` went through in memory. With an id of `PoolState`, you can let the pool recover to that state.\n\n```typescript\n// current state: foo\nlet poolStateId: string = configurableCorePool.getPoolState().id;\n\n// some transactions...\n// current state: bar\n\nconfigurableCorePool.recover(poolStateId);\n// current state: foo\n```\n\nOr just let the pool step back to last state.\n\n```typescript\n// current state: foo\nlet poolStateId: string = configurableCorePool.getPoolState().id;\n\n// one transaction...\n// current state: bar\n\nconfigurableCorePool.stepBack();\n// current state: foo\n```\n\nSometime we want multiple instances to try out various of possibilities from current state, it's good time using `fork` to do that.\n\n```typescript\nlet forkedConfigurableCorePool: ConfigurableCorePool =\n  configurableCorePool.fork();\n\n// some transactions with configurableCorePool...\n\n// some transactions with forkedConfigurableCorePool...\n```\n\nThe forked pool will remain the same state as its parent and the two pools act independently.\n\nNote: `poolState.fromTransition` of a pool built from a `PoolConfig` or recovered from a snapshot will be `undefined` while `poolState.fromTransition` of a forked pool will have a `Record` with `ActionType.FORK` and its parent as source `PoolState`.\n"
  },
  {
    "path": "docs/getting-a-core-pool-instance.md",
    "content": "### Getting a Core Pool instance\n\nA simple way to get a Core Pool instance is to build a new one according to the `PoolConfig`.\n\n```typescript\nlet configurableCorePool: IConfigurableCorePool =\n  clientInstance.initCorePoolFromConfig(\n    new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)\n  );\n```\n\nAnd then don't forget to initialize that before executing any interaction.\n\n```typescript\nlet sqrtPriceX96ForInitialization = JSBI.BigInt(\"4295128739\");\nawait configurableCorePool.initialize(sqrtPriceX96ForInitialization);\n```\n"
  },
  {
    "path": "docs/getting-a-pool-instance-with-the-data-fetched.md",
    "content": "### Getting a pool instance with the data fetched\n\nUsually before work begins, the preparation includes the following steps:\n\n1. Using mainnet data to initialize a core pool\n2. Replaying events up to the specified block\n\n`SimulatorClient`(`clientInstance.initCorePoolFromMainnet` and `clientInstance.recoverFromMainnetEventDBFile`) is designed to do that. See [Fetching all the data of a certain pool from Ethereum](fetching-all-the-data-of-a-certain-pool-from-ethereum.md).\n\nIf you have tried `MainnetDataDownloader` to download or update event logs(See [Uniswap-v3-Events-Downloader](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Events-Downloader)), then you are supposed to get the pool instance in this way:\n\n```typescript\n// the database name containing downloaded-and-pre-processed mainnet events\nlet mainnetEventDBFilePath =\n  \"events_0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8.db\";\n\nlet simulationDataManager: SimulationDataManager =\n  await SQLiteSimulationDataManager.buildInstance(\n    \"Your file path to save the internal data\"\n  );\nlet clientInstance: SimulatorClient = new SimulatorClient(\n  simulationDataManager\n);\n\n// Specify an endBlock number\n// the SimulatorClient will replay events up to that block\nlet endBlock = 12374077;\n\nlet configurableCorePool: ConfigurableCorePool =\n  await clientInstance.recoverFromMainnetEventDBFile(\n    mainnetEventDBFilePath,\n    endBlock\n  );\n\n// Now you can do whatever you want with the simulated pool\n// here we simply print out the square root price of the pool at the specified block height\nconsole.log(configurableCorePool.getCorePool().sqrtPriceX96.toString());\n```\n"
  },
  {
    "path": "docs/how-tuner-library-works.md",
    "content": "### How \"Tuner\" Library Works?\n\nThe overall design of the simulator consists of several components (rough dependency graph shown below), this **does NOT** necessarily 100% reflect how everything is implemented, but to give you an intuitive understanding of the relationships between the components and their corresponding purposes:\n\n**`SimulatorClient`** - _The high-level and easiest way to use the simulator_\n\n&#x20; |\\_\\_ **`ConfigurableCorePool`** - _To give **`CorePool`** snapshot and roadmap capabilities_\n\n&#x20; |\\_\\_ **`CorePool`** - _The re-implementation of_ [_Uniswap-V3-Core logic_](https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol)\n\n&#x20; |\\_\\_ **`MainnetDataDownloader`** - _The download-and-update utility to retrieve mainnet events_\n\n&#x20; |\\_\\_ **`EventDBManager`** - _To persist mainnet event data using SQLite_\n\n&#x20; |\\_\\_ **`SimulatorRoadMapManager`** - _To take snapshots and do state-change roadmap tracking_\n\n&#x20; |\\_\\_ **`SimulationDataManager`** - _To persist snapshots and roadmaps using SQLite_\n\nThere are 2 abstraction layers of the library:\n\n#### Top-Level: [`SimulatorClient`](src/client/SimulatorClient.ts)\n\nThis is a convenient way to [get you started](./#quick-start) immediately. We have wrapped the underlying logic of data downloading, data pre-processing, and simulated Uniswap V3 pool together instantiation together, so you don't have to deal with the details.\n\n#### Low-Level:&#x20;\n\nIf you want more flexible ways to run your own simulation scenarios and have more granular control, you can wire things up manually as you like, by directly interacting with the below classes:\n\n- **`ConfigurableCorePool`**\n- **`MainnetDataDownloader`**\n- **`SimulatorRoadMapManager`**\n\n####\n"
  },
  {
    "path": "docs/installing-tuner.md",
    "content": "### Installing \"Tuner\"\n\n{% embed url=\"https://www.npmjs.com/package/@bella-defintech/uniswap-v3-simulator\" %}\n\n```bash\nyarn add @bella-defintech/uniswap-v3-simulator\n```\n\n```bash\nyarn upgrade @bella-defintech/uniswap-v3-simulator\n```\n"
  },
  {
    "path": "docs/interacting-with-core-pool.md",
    "content": "### Interacting with Core Pool\n\nA `CorePool` corresponds to a contract of Uniswap V3 Core Pool. You can inspect its state like `sqrtPriceX96`, `tickCurrent`, `lquidity` and more detailed information on any `Tick` or `Position`.\n\n```typescript\nlet corePoolView: CorePoolView = configurableCorePool.getCorePool();\n\ncorePoolView.tickCurrent;\n\ncorePoolView.getTick(tickIndex);\n\ncorePoolView.getPosition(owner, tickLower, tickUpper);\n```\n\nAlso, you can interact with the pool by `mint`, `burn` and `swap`. However, while interacting with the pool, we recommend using `ConfigurableCorePool` to get full functionality of the Tuner.\n\nBased on the `CorePool`, a `ConfigurableCorePool` is a state machine providing utilities like post-processor, fork, snapshot, step back and recover.\n\n```typescript\nlet amount0: JSBI, amount1: JSBI;\n\n({ amount0, amount1 } = await configurableCorePool.mint(\n  testUser,\n  tickLower,\n  tickUpper,\n  liquidity\n));\n\n({ amount0, amount1 } = await configurableCorePool.burn(\n  testUser,\n  tickLower,\n  tickUpper,\n  liquidity\n));\n\n({ amount0, amount1 } = await configurableCorePool.swap(\n  zeroForOne,\n  amountSpecified,\n  sqrtPriceLimitX96\n));\n```\n\nSince Tuner is focusing on the math model instead of token or stratgy, feel free to `mint`, `burn` and `swap` as you like, then observe whether things go as you expect.\n"
  },
  {
    "path": "docs/loading-and-streaming-events-into-a-pool.md",
    "content": "### Loading and streaming events into a pool\n\nSuppose a quant developer is going to backtest some strategy on mainnet pool.\n\nThis includes the following steps:\n\n1. Using mainnet data to initialize a core pool\n2. Replaying events up to a certain block as a checkpoint\n3. Do some interaction as the strategy asks as interpolation to real transactions\n4. Replaying events until the next checkpoint\n5. Repeat step3-4 until events are run out\n6. Evaluate performance of the strategy\n\nTuner has offered an example project called `uniswap-v3-bot`. It similarly follow the process above to build a strategy platform for backtesting, dry-run and run, where a user adds a new strategy by implementing some callback interfaces(`trigger`, `cache`, `act` and `evaluate`). See [Uniswap-v3-Strategy-Backtest](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Strategy-Backtest).\n\nWhen getting a core pool instance by `clientInstance.initCorePoolFromMainnet` or `clientInstance.recoverFromMainnetEventDBFile`, Tuner has already automatically replayed events from the first record to the last one in `endBlock`. For replaying following events, you can load them by `EventDBManager`, and decide how to use them on your own.\n\nAn example of streaming process will be:\n\n```typescript\nimport {\n  ConfigurableCorePool,\n  EventDBManager,\n  EventType,\n  SimulationDataManager,\n  SimulatorClient,\n  SQLiteSimulationDataManager,\n} from \"@bella-defintech/uniswap-v3-simulator\";\nimport { LiquidityEvent } from \"@bella-defintech/uniswap-v3-simulator/dist/entity/LiquidityEvent\";\nimport { SwapEvent } from \"@bella-defintech/uniswap-v3-simulator/dist/entity/SwapEvent\";\nimport JSBI from \"jsbi\";\n\n// the database name containing downloaded-and-pre-processed mainnet events\nlet mainnetEventDBFilePath =\n  \"events_0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8.db\";\n\n// build a client instance\nlet simulationDataManager: SimulationDataManager =\n  await SQLiteSimulationDataManager.buildInstance(\n    \"Your file path to save the internal data\"\n  );\nlet clientInstance: SimulatorClient = new SimulatorClient(\n  simulationDataManager\n);\n\n// Specify an endBlock number\n// the SimulatorClient will replay events up to that block\nlet endBlock0 = 12374077;\n\n// get a pool instance\nlet configurableCorePool: ConfigurableCorePool =\n  await clientInstance.recoverFromMainnetEventDBFile(\n    mainnetEventDBFilePath,\n    endBlock0\n  );\n\n// get an EventDBManager instance to load events\nlet eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);\n\n// get and sort event by block number\nlet events: (LiquidityEvent | SwapEvent)[] = [];\nlet startBlock = 1000,\n  endBlock = 2000;\nlet mintEvents: LiquidityEvent[] =\n  await eventDB.getLiquidityEventsByBlockNumber(\n    EventType.MINT,\n    startBlock,\n    endBlock\n  );\nlet burnEvents: LiquidityEvent[] =\n  await eventDB.getLiquidityEventsByBlockNumber(\n    EventType.BURN,\n    startBlock,\n    endBlock\n  );\nlet swapEvents: SwapEvent[] = await eventDB.getSwapEventsByBlockNumber(\n  startBlock,\n  endBlock\n);\nevents.push(...mintEvents);\nevents.push(...burnEvents);\nevents.push(...swapEvents);\nevents.sort(function (a, b) {\n  return a.blockNumber == b.blockNumber\n    ? a.logIndex - b.logIndex\n    : a.blockNumber - b.blockNumber;\n});\n\n// replay events\nfor (let index = 0; index < events.length; index++) {\n  // avoid stack overflow for the possible recovering operation\n  if (index % 1000 == 0) {\n    configurableCorePool.takeSnapshot(\"\");\n  }\n\n  let event = events[index];\n  switch (event.type) {\n    case EventType.MINT:\n      await configurableCorePool.mint(\n        event.recipient,\n        event.tickLower,\n        event.tickUpper,\n        event.liquidity\n      );\n      break;\n    case EventType.BURN:\n      await configurableCorePool.burn(\n        event.msgSender,\n        event.tickLower,\n        event.tickUpper,\n        event.liquidity\n      );\n      break;\n    case EventType.SWAP:\n      let zeroForOne: boolean = JSBI.greaterThan(event.amount0, JSBI.BigInt(0))\n        ? true\n        : false;\n      await configurableCorePool.swap(\n        zeroForOne,\n        event.amountSpecified\n        //,event.sqrt_price_x96\n      );\n      break;\n    default:\n      // @ts-ignore: ExhaustiveCheck\n      const exhaustiveCheck: never = event;\n  }\n}\n```\n\nNote: Tuner doesn't export `LiquidityEvent` | `SwapEvent` directly but you can sitll use them. For external data(event logs), we recommend you to implement your `EventDBManager` to load and manage event logs, according to your context.\n"
  },
  {
    "path": "docs/performance.md",
    "content": "### Performance\n\nEnvironment: AWS EC2 t3.xlarge on us-east-1, RPC provider by Alchemy, Subgraph by hosted service on the Graph\n\nDownloading 260,000+ events of WETH-USDC-3000(upper-middle transaction volume within all Uniswap v3 core pools) until #14027607 (Jan 18, 2022 approximately):\n\n- Tuner v0.1.3 **461.6m(>7 hour)**\n- Tuner v0.1.4 **51.1m(<1 hour)**\n\nReplaying over 124,000 mainnet events: **within 16 second**\n"
  },
  {
    "path": "docs/persisting-and-recovering.md",
    "content": "### Persisting & Recovering\n\nIf a state is important enough that you want to test something on it across multiple programs, e.g. suppose you want to test something important on the state after replaying 200,000 events from the deployment of a pool, you can replay those events for once, then persist everything as a snapshot and directly resume from then on to make the preparation faster.\n\n```typescript\nconfigurableCorePool.takeSnapshot(\"description for snapshot\");\n```\n\nThen don't forget to persist it so that you can recover/resume later.\n\n```typescript\nlet snapshotId: string = await configurableCorePool.persistSnapshot();\n```\n\nLater you can recover the pool from any snapshot in the internal database(local database file with default SQLite implementation).\n\n```typescript\nlet recoveredConfigurableCorePool: ConfigurableCorePool =\n  await clientInstance.recoverCorePoolFromSnapshot(snapshotId);\n```\n\nIf you forget the snapshotId, you can list and check all snapshots by information like description or created timestamp.\n\n```typescript\nlet snapshotProfiles: SnapshotProfile[] =\n  await clientInstance.listSnapshotProfiles();\n```\n"
  },
  {
    "path": "docs/pool-state-and-transition.md",
    "content": "### PoolState & Transition\n\nA `ConfigurableCorePool` is a state machine based on the math model aka `CorePool` of Uniswap v3 contract implementation.\n\nEvery core pool state corresponds to a `PoolState`. Every interaction(`mint`, `burn`, `swap`, `collect` and `fork`) corresponds to a `Transition`. A `Record` contains information about the action. A `Transition` makes a `PoolState` move to next `PoolState` according to the `Record`.\n"
  },
  {
    "path": "docs/post-processor.md",
    "content": "### Post-processor\n\nA post-processor registered in `ConfigurableCorePool` works as an interceptor to execute callback function after executing an interaction. You can customize your handler function with `ConfigurableCorePool`(pool state after the interaction) and `TransitionView` exposed by the post-processor.\n\nTake for example, when locating errors, you can check the state after executing an interaction from a large number of events, take the snapshot, think about what happened exactly and resume from that state later.\n\nYou can set a post-processor after every interaction in this way.\n\n```typescript\nconfigurableCorePool.updatePostProcessor(\n  (pool: ConfigurableCorePool, transition: TransitionView) => {\n    console.log(pool.getPoolState().id);\n    console.log(transition.getRecord().id);\n    return Promise.resolve();\n  }\n);\n```\n\nOr set it with a specific interaction.\n\n```typescript\nawait configurableCorePool.mint(\n  \"0x01\",\n  -887272,\n  887272,\n  JSBI.BigInt(\"10860507277202\"),\n  (pool: ConfigurableCorePool, transition: TransitionView) => {\n    console.log(pool.getPoolState().id);\n    console.log(transition.getRecord().id);\n    return Promise.resolve();\n  }\n);\n```\n\nNote: If any error happens during the interaction or the post process, the `ConfigurableCorePool` will recover as the state before that interaction happened, just like contract transaction reverts.\n"
  },
  {
    "path": "docs/quick-start.md",
    "content": "### Quick Start\n\n```typescript\nimport {\n  SimulationDataManager,\n  SimulatorClient,\n  SQLiteSimulationDataManager,\n} from \"@bella-defintech/uniswap-v3-simulator\";\n\n// 1. Instantiate a SimulationDataManager\n// this is for handling the internal data (snapshots, roadmaps, etc.)\nlet simulationDataManager: SimulationDataManager =\n  await SQLiteSimulationDataManager.buildInstance(\n    \"Your file path to save the internal data\"\n  );\nlet clientInstance: SimulatorClient = new SimulatorClient(\n  simulationDataManager\n);\n\n// 2. Initialize a pool simulation from mainnet\n// specify the core pool you want and time range of event logs to sync\nlet poolName = \"test\";\n\n// This would be the contract address of a certain Uniswap V3 Pool\nlet poolAddress = \"0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8\";\n\n// Specify an endBlock number\n// the SimulatorClient will download data up to that block\n// and save it to a sqlite database file locally\nlet endBlock = 12374077;\n\n// 3. This method helps you:\n//    Download event data of a certain Uniswap V3 pool from mainnet\n//    Pre-process the data to figure out the inputs of swap events\nawait clientInstance.initCorePoolFromMainnet(\n  poolName,\n  poolAddress,\n  \"afterDeployment\"\n);\n\n// 4. Build a simulated CorePool instance from the downloaded-and-pre-processed mainnet events\nlet configurableCorePool = await clientInstance.recoverFromMainnetEventDBFile(\n  `${poolName}_${poolAddress}.db`,\n  endBlock\n);\n\n// 5. Now you can do whatever you want with the simulated pool\n//    here we simply print out the square root price of the pool at the specified block height\nconsole.log(configurableCorePool.getCorePool().sqrtPriceX96.toString());\n```\n"
  },
  {
    "path": "docs/simulator-roadmap-manager.md",
    "content": "### SimulatorRoadmapManager\n\nIf we take every `ConfigurableCorePool` as a state machine, then a `SimulatorRoadmapManager` can be taken as `PoolState` container as well as `ConfigurableCorePool` manager. At this point, every state machine can be expanded as a roadmap of states.\n\nFirst let's get the instance of `SimulatorRoadmapManager` from `SimulatorClient`.\n\n```typescript\nlet simulatorRoadmapManager: SimulatorRoadmapManager =\n  clientInstance.simulatorRoadmapManager;\n```\n\nYou can list and check all `ConfigurableCorePool` created by Tuner within the program.\n\n```typescript\nlet pools: ConfigurableCorePool[] = await simulatorRoadmapManager.listRoutes();\n```\n\nWith a `ConfigurableCorePool` id, Tuner can print route from the first pool state to current pool state of the state machine in pretty format.\n\n```typescript\nawait simulatorRoadmapManager.printRoute(configurableCorePool.id);\n```\n\nAlso you can persist the route for selecting them in the internal database later.\n\n```typescript\nlet roadmapId = await simulatorRoadmapManager.persistRoute(\n  configurableCorePool.id,\n  \"description for roadmap\"\n);\n```\n\nThen load the roadmap and print the route in pretty format.\n\n```typescript\nawait simulatorRoadmapManager.loadAndPrintRoute(roadmapId);\n```\n\nNote: As the storage scale of Uniswap V3 CorePool(up to 160,000+ Ticks and unlimited Positions), frequent persistence of route as well as snapshot is not recommended. Please pay attention to space cost.\n"
  },
  {
    "path": "examples/Uniswap-v3-Events-Downloader/EventsDownloader.ts",
    "content": "import { EndBlockTypeWhenInit, BSCDataDownloader } from \"../../src\";\nimport { EventDataSourceType } from \"../../src/enum/EventDataSourceType\";\nimport dotenv from \"dotenv\";\nimport * as log4js from \"log4js\";\n\nlog4js.configure({\n  appenders: {\n    out: { type: \"stdout\" },\n    app: { type: \"fileSync\", filename: \"EventsDownloader.log\" },\n  },\n  categories: { default: { appenders: [\"out\", \"app\"], level: \"info\" } },\n});\n\nconst logger = log4js.getLogger(\"EventsDownloader\");\n\n// load .env file\ndotenv.config();\n\nasync function main() {\n  // eth/usdt\n  let poolName = \"eth\";\n  let poolAddress = \"0xBe141893E4c6AD9272e8C04BAB7E6a10604501a5\";\n  let deploymentBlockNumber = 27254278;\n\n  // let endBlock: EndBlockTypeWhenInit = \"latest\";\n\n  // let endBlock: EndBlockTypeWhenInit = \"afterInitialization\";\n  // It will use RPCProviderUrl in tuner.config.js if this is undefined.\n  let RPCProviderUrl: string | undefined = undefined;\n  // You can specify data source of events here, and Uniswap v3 Subgraph as default is recommended rather than RPC for at least 75% time saving.\n  // Just a reminder, RPC endpoint is necessary for the simulator even if you choose to download events from Subgraph.\n  let eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH;\n  let mainnetDataDownloader = new BSCDataDownloader(\n    RPCProviderUrl,\n    eventDataSourceType\n  );\n\n  await mainnetDataDownloader.download(\n    poolName,\n    poolAddress,\n    deploymentBlockNumber,\n    deploymentBlockNumber\n  );\n}\n\nmain()\n  .then(() => process.exit(0))\n  .catch((error) => {\n    logger.error(error);\n    process.exit(1);\n  });\n\nprocess.on(\"exit\", () => {\n  log4js.shutdown(() => {\n    console.log(\"log has been flushed and closed\");\n  });\n});\n"
  },
  {
    "path": "examples/Uniswap-v3-Events-Downloader/EventsUpdater.ts",
    "content": "import {\n  EndBlockTypeWhenRecover,\n  BSCDataDownloader,\n  EventDBManager,\n} from \"../../src\";\nimport { EventDataSourceType } from \"../../src/enum/EventDataSourceType\";\nimport dotenv from \"dotenv\";\nimport * as log4js from \"log4js\";\n\n// load .env file\ndotenv.config();\n\nlog4js.configure({\n  appenders: {\n    out: { type: \"stdout\", level: \"info\" },\n    app: { type: \"fileSync\", filename: \"EventsUpdater.log\", level: \"info\" },\n  },\n  categories: { default: { appenders: [\"out\", \"app\"], level: \"info\" } },\n});\n\nconst logger = log4js.getLogger(\"EventsUpdater\");\n\nasync function main() {\n  // let endBlock: EndBlockTypeWhenRecover = \"latestDownloaded\";\n  // It will use RPCProviderUrl in tuner.config.js if this is undefined.\n  let RPCProviderUrl: string | undefined = undefined;\n  // You can specify data source of events here, and Uniswap v3 Subgraph as default is recommended rather than RPC for at least 75% time saving.\n  // Just a reminder, RPC endpoint is necessary for the simulator even if you choose to download events from Subgraph.\n  let eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH;\n  let mainnetEventDBFilePath =\n    \"eth_0xBe141893E4c6AD9272e8C04BAB7E6a10604501a5.db\";\n  let mainnetDataDownloader = new BSCDataDownloader(\n    RPCProviderUrl,\n    eventDataSourceType\n  );\n\n  let endBlock: EndBlockTypeWhenRecover = \"latestOnChain\";\n  await mainnetDataDownloader.update(mainnetEventDBFilePath, endBlock);\n\n  // await mainnetDataDownloader.replaceLiquidityEventsFromSubgraphToRPC(\n  //   mainnetEventDBFilePath\n  // );\n\n  // Pre-process events only\n  // let { poolAddress } = mainnetDataDownloader.parseFromMainnetEventDBFilePath(\n  //   mainnetEventDBFilePath\n  // );\n  // let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);\n  // await mainnetDataDownloader.preProcessEvents(poolAddress, eventDB);\n}\n\nmain()\n  .then(() => process.exit(0))\n  .catch((error) => {\n    logger.error(error);\n    process.exit(1);\n  });\n\nprocess.on(\"exit\", () => {\n  log4js.shutdown(() => {\n    console.log(\"log has been flushed and closed\");\n  });\n});\n"
  },
  {
    "path": "examples/Uniswap-v3-Strategy-Backtest/README.md",
    "content": "# Bella-DeFinTech / uniswap-v3-bot\n\n- We have made a demo as a platform to build and backtest strategy based on the Tuner.\n\n- For reference: https://github.com/Bella-DeFinTech/uniswap-v3-bot/blob/main/test/Backtest.test.ts\n"
  },
  {
    "path": "hardhat.config.ts",
    "content": "import \"@typechain/hardhat\";\nimport \"@nomiclabs/hardhat-ethers\";\nimport \"@nomiclabs/hardhat-waffle\";\n\nexport default {\n  defaultNetwork: \"hardhat\",\n  networks: {\n    mainnet: {\n      url: process.env.MAINNET_PROVIDER_URL,\n    },\n    hardhat: {\n      forking: {\n        url: process.env.FORKING_PROVIDER_URL,\n        blockNumber: 13578942,\n      },\n      mining: {\n        auto: true,\n        interval: 0,\n      },\n      allowUnlimitedContractSize: false,\n    },\n  },\n  paths: {\n    sources: \"./test/contracts/src\",\n    tests: \"./test/contracts\",\n    cache: \"./test/cache\",\n    artifacts: \"./test/artifacts\",\n  },\n  solidity: {\n    version: \"0.7.6\",\n    settings: {\n      optimizer: {\n        enabled: true,\n        runs: 1, // 800,\n      },\n      metadata: {\n        bytecodeHash: \"none\",\n      },\n    },\n  },\n  mocha: {\n    timeout: 3600000,\n  },\n  typechain: {\n    outDir: \"test/typechain\",\n    target: \"ethers-v5\",\n    alwaysGenerateOverloads: false,\n  },\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@bella-defintech/uniswap-v3-simulator\",\n  \"version\": \"0.1.9\",\n  \"description\": \"the 'Tuner', a Uniswap V3 Simulator\",\n  \"keywords\": [\n    \"uniswap\",\n    \"uniswapv3\",\n    \"uniswap-v3\",\n    \"simulation\",\n    \"simulator\",\n    \"defi\"\n  ],\n  \"main\": \"dist/index.js\",\n  \"typings\": \"dist/index.d.ts\",\n  \"files\": [\n    \"dist/**\",\n    \"abi/**\",\n    \"tuner.config.js\"\n  ],\n  \"scripts\": {\n    \"generate-types\": \"typechain --target=ethers-v5 --out-dir src/typechain 'abi/*.json'\",\n    \"build\": \"tsc\",\n    \"test\": \"yarn mocha\",\n    \"lint\": \"yarn prettier --write .\",\n    \"clean\": \"hardhat clean\",\n    \"compile\": \"hardhat compile && yarn generate-types\",\n    \"script\": \"yarn ts-node\",\n    \"testcontracts\": \"hardhat test\"\n  },\n  \"license\": \"BUSL-1.1\",\n  \"devDependencies\": {\n    \"@nomiclabs/hardhat-ethers\": \"^2.0.2\",\n    \"@nomiclabs/hardhat-waffle\": \"^2.0.1\",\n    \"@typechain/ethers-v5\": \"^7.2.0\",\n    \"@typechain/hardhat\": \"^2.3.1\",\n    \"@types/chai\": \"^4.2.21\",\n    \"@types/chai-as-promised\": \"^7.1.4\",\n    \"@types/mocha\": \"^9.0.0\",\n    \"@types/sinon\": \"^10.0.4\",\n    \"@types/sinon-chai\": \"^3.2.5\",\n    \"@types/sqlite3\": \"^3.1.7\",\n    \"@types/uuid\": \"^8.3.1\",\n    \"@uniswap/v3-core\": \"^1.0.0\",\n    \"chai\": \"^4.3.4\",\n    \"chai-as-promised\": \"^7.1.1\",\n    \"ethereum-waffle\": \"^3.0.0\",\n    \"hardhat\": \"^2.6.0\",\n    \"mocha\": \"^9.0.3\",\n    \"mocha-chai-jest-snapshot\": \"^1.1.3\",\n    \"mochawesome\": \"^6.2.2\",\n    \"prettier\": \"2.3.2\",\n    \"sinon\": \"^11.1.2\",\n    \"sinon-chai\": \"^3.7.0\",\n    \"ts-node\": \"^10.2.0\",\n    \"typechain\": \"^5.2.0\",\n    \"typescript\": \"^4.3.5\"\n  },\n  \"dependencies\": {\n    \"bignumber.js\": \"^9.0.2\",\n    \"dayjs\": \"^1.10.6\",\n    \"dotenv\": \"^17.2.1\",\n    \"ethers\": \"^5.7.0\",\n    \"graphql\": \"^16.2.0\",\n    \"graphql-request\": \"^3.7.0\",\n    \"jsbi\": \"3.1.6\",\n    \"knex\": \"^0.95.10\",\n    \"log4js\": \"^6.9.1\",\n    \"reflect-metadata\": \"^0.1.13\",\n    \"sqlite3\": \"^5.0.2\",\n    \"typedjson\": \"^1.7.0\",\n    \"uuid\": \"^8.3.2\"\n  },\n  \"resolutions\": {\n    \"tar\": \"^4.4.18\"\n  },\n  \"mocha\": {\n    \"colors\": true,\n    \"spec\": [\n      \"test/*.{test,spec}.ts\"\n    ],\n    \"reporter\": [\n      \"mochawesome\"\n    ],\n    \"require\": [\n      \"ts-node/register\"\n    ],\n    \"timeout\": 360000000\n  }\n}\n"
  },
  {
    "path": "src/client/BSCDataDownloader.ts",
    "content": "import { EventType } from \"../enum/EventType\";\nimport { EventDBManager } from \"../manager/EventDBManager\";\nimport { BigNumber, providers } from \"ethers\";\nimport { UniswapV3Pool2__factory as UniswapV3PoolFactory } from \"../typechain/factories/UniswapV3Pool2__factory\";\nimport { UniswapV3Pool2 as UniswapV3Pool } from \"../typechain/UniswapV3Pool2\";\nimport {\n  ConfigurableCorePool,\n  PoolConfig,\n  convertTokenStr,\n  exists,\n  getDatabaseNameFromPath,\n} from \"..\";\nimport { LiquidityEvent } from \"../entity/LiquidityEvent\";\nimport { SwapEvent } from \"../entity/SwapEvent\";\nimport { SQLiteSimulationDataManager } from \"../manager/SQLiteSimulationDataManager\";\nimport { SimulationDataManager } from \"../interface/SimulationDataManager\";\nimport { printParams } from \"../util/Serializer\";\nimport JSBI from \"jsbi\";\nimport {\n  BSC_PANCAKE_V3_SUBGRAPH_ENDPOINT,\n  SUBGRAPH_API_KEY,\n  ZERO,\n} from \"../enum/InternalConstants\";\nimport { EventDataSourceType } from \"../enum/EventDataSourceType\";\nimport { PoolState } from \"../model/PoolState\";\nimport { ConfigurableCorePool as ConfigurableCorePoolImpl } from \"../core/ConfigurableCorePool\";\nimport { SimulatorConsoleVisitor } from \"../manager/SimulatorConsoleVisitor\";\nimport { SimulatorPersistenceVisitor } from \"../manager/SimulatorPersistenceVisitor\";\nimport { SimulatorRoadmapManager } from \"../manager/SimulatorRoadmapManager\";\nimport {\n  EndBlockTypeWhenInit,\n  EndBlockTypeWhenRecover,\n} from \"../entity/EndBlockType\";\nimport { loadConfig } from \"../config/TunerConfig\";\nimport { request, gql } from \"graphql-request\";\nimport { PositionManager } from \"../manager/PositionManager\";\nimport * as log4js from \"log4js\";\nimport { TypedEvent } from \"../typechain/common\";\n\nconst logger = log4js.getLogger(\"BSCDataDownloader\");\n\nconst headers = {\n  Authorization: `Bearer ${SUBGRAPH_API_KEY}`,\n};\n\nexport class BSCDataDownloader {\n  private RPCProvider: providers.JsonRpcProvider;\n\n  private eventDataSourceType: EventDataSourceType;\n\n  constructor(\n    RPCProviderUrl: string | undefined,\n    eventDataSourceType: EventDataSourceType\n  ) {\n    if (RPCProviderUrl == undefined) {\n      let tunerConfig = loadConfig(undefined);\n      RPCProviderUrl = tunerConfig.RPCProviderUrl;\n    }\n    this.RPCProvider = new providers.JsonRpcProvider(RPCProviderUrl);\n    this.eventDataSourceType = eventDataSourceType;\n  }\n\n  async queryDeploymentBlockNumber(poolAddress: string): Promise<number> {\n    // TODO how to know accurate block number on contract deployment?\n    // Maybe use etherscan API or scan back mainnet trxs through the first event the contract emitted.\n    // BTW, for most cases, it's the same as Initialization event block number. Let's take this now.\n    return this.queryInitializationBlockNumber(poolAddress);\n  }\n\n  async queryInitializationBlockNumber(poolAddress: string): Promise<number> {\n    let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);\n    let initializeTopic = uniswapV3Pool.filters.Initialize();\n    let initializationEvent = await this.queryFilterWithRetry(\n      uniswapV3Pool,\n      initializeTopic\n    );\n    return initializationEvent[0].blockNumber;\n  }\n\n  async parseEndBlockTypeWhenInit(\n    toBlock: EndBlockTypeWhenInit,\n    poolAddress: string\n  ): Promise<number> {\n    switch (toBlock) {\n      case \"latest\":\n        return (await this.RPCProvider.getBlock(\"latest\")).number;\n      case \"afterDeployment\":\n        return await this.queryDeploymentBlockNumber(poolAddress);\n      case \"afterInitialization\":\n        return await this.queryInitializationBlockNumber(poolAddress);\n      default:\n        let latestOnChain = (await this.RPCProvider.getBlock(\"latest\")).number;\n        return toBlock > latestOnChain ? latestOnChain : toBlock;\n    }\n  }\n\n  async parseEndBlockTypeWhenRecover(\n    latestDownloadedEventBlockNumber: number,\n    toBlock: EndBlockTypeWhenRecover,\n    poolAddress: string\n  ): Promise<number> {\n    switch (toBlock) {\n      case \"latestOnChain\":\n        return (await this.RPCProvider.getBlock(\"latest\")).number;\n      case \"latestDownloaded\":\n        return latestDownloadedEventBlockNumber;\n      case \"afterDeployment\":\n        return await this.queryDeploymentBlockNumber(poolAddress);\n      case \"afterInitialization\":\n        return await this.queryInitializationBlockNumber(poolAddress);\n      default:\n        let latestOnChain = (await this.RPCProvider.getBlock(\"latest\")).number;\n        return toBlock > latestOnChain ? latestOnChain : toBlock;\n    }\n  }\n\n  generateMainnetEventDBFilePath(\n    poolName: string,\n    poolAddress: string\n  ): string {\n    return `${poolName}_${poolAddress}.db`;\n  }\n\n  parseFromMainnetEventDBFilePath(filePath: string): {\n    poolName: string;\n    poolAddress: string;\n  } {\n    let databaseName = getDatabaseNameFromPath(filePath, \".db\");\n    let nameArr = databaseName.split(\"_\");\n    return { poolName: nameArr[0], poolAddress: nameArr[1] };\n  }\n\n  async download(\n    poolName: string = \"\",\n    poolAddress: string,\n    deploymentBlockNumber: number,\n    toBlock: EndBlockTypeWhenInit,\n    batchSize: number = 10000\n  ) {\n    // check toBlock first\n    let toBlockAsNumber = await this.parseEndBlockTypeWhenInit(\n      toBlock,\n      poolAddress\n    );\n\n    let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);\n    if (toBlockAsNumber < deploymentBlockNumber)\n      throw new Error(\n        `The pool does not exist at block height: ${toBlockAsNumber}, it was deployed at block height: ${deploymentBlockNumber}`\n      );\n\n    let initializeTopic = uniswapV3Pool.filters.Initialize();\n    let initializationEvent = await this.queryFilterWithRetry(\n      uniswapV3Pool,\n      initializeTopic,\n      deploymentBlockNumber,\n      toBlockAsNumber\n    );\n    let initializationSqrtPriceX96 = initializationEvent[0].args.sqrtPriceX96;\n    let initializationEventBlockNumber = initializationEvent[0].blockNumber;\n\n    // check db file then\n    let filePath = this.generateMainnetEventDBFilePath(poolName, poolAddress);\n    if (exists(filePath))\n      throw new Error(\n        `The database file: ${filePath} already exists. You can either try to update or delete the database file.`\n      );\n\n    let eventDB = await EventDBManager.buildInstance(filePath);\n    try {\n      // query and record poolConfig\n      let poolConfig = new PoolConfig(\n        await uniswapV3Pool.tickSpacing(),\n        await uniswapV3Pool.token0(),\n        await uniswapV3Pool.token1(),\n        await uniswapV3Pool.fee()\n      );\n      await eventDB.addPoolConfig(poolConfig);\n      await eventDB.saveLatestEventBlockNumber(deploymentBlockNumber);\n\n      if (toBlock === \"afterDeployment\") return;\n\n      // record initialize event\n      await eventDB.addInitialSqrtPriceX96(\n        initializationSqrtPriceX96.toString()\n      );\n      await eventDB.saveInitializationEventBlockNumber(\n        initializationEventBlockNumber\n      );\n      await eventDB.saveLatestEventBlockNumber(initializationEventBlockNumber);\n\n      if (toBlock === \"afterInitialization\") return;\n\n      // download events after initialization\n      if (this.eventDataSourceType === EventDataSourceType.SUBGRAPH) {\n        await this.downloadEventsFromSubgraph(\n          uniswapV3Pool,\n          poolAddress.toLowerCase(),\n          poolConfig!.token0,\n          poolConfig!.token1,\n          eventDB,\n          initializationEventBlockNumber,\n          toBlockAsNumber,\n          batchSize\n        );\n      } else if (this.eventDataSourceType === EventDataSourceType.RPC) {\n        await this.downloadEventsFromRPC(\n          uniswapV3Pool,\n          eventDB,\n          initializationEventBlockNumber,\n          toBlockAsNumber,\n          batchSize\n        );\n      }\n      // await this.preProcessEvents(poolAddress, eventDB);\n    } finally {\n      await eventDB.close();\n    }\n  }\n\n  async replaceLiquidityEventsFromSubgraphToRPC(\n    mainnetEventDBFilePath: string,\n    batchSize: number = 10000\n  ) {\n    let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);\n\n    let initializationEventBlockNumber =\n      await eventDB.getInitializationEventBlockNumber();\n    let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();\n    // remove incomplete events\n    await eventDB.deleteLiquidityEventsByBlockNumber(\n      EventType.MINT,\n      initializationEventBlockNumber,\n      latestEventBlockNumber\n    );\n    await eventDB.deleteLiquidityEventsByBlockNumber(\n      EventType.BURN,\n      initializationEventBlockNumber,\n      latestEventBlockNumber\n    );\n\n    // download events after initialization\n    let { poolAddress } = this.parseFromMainnetEventDBFilePath(\n      mainnetEventDBFilePath\n    );\n\n    let poolConfig = await eventDB.getPoolConfig();\n\n    let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);\n\n    await this.downloadEventsFromSubgraph(\n      uniswapV3Pool,\n      poolAddress.toLowerCase(),\n      poolConfig!.token0,\n      poolConfig!.token1,\n      eventDB,\n      initializationEventBlockNumber,\n      latestEventBlockNumber,\n      batchSize,\n      true\n    );\n  }\n\n  async update(\n    mainnetEventDBFilePath: string,\n    toBlock: EndBlockTypeWhenRecover,\n    batchSize: number = 10000\n  ) {\n    // check dbfile first\n    let { poolAddress } = this.parseFromMainnetEventDBFilePath(\n      mainnetEventDBFilePath\n    );\n    if (!exists(mainnetEventDBFilePath))\n      throw new Error(\n        `The database file: ${mainnetEventDBFilePath} does not exist. Please download the data first.`\n      );\n\n    // check toBlock then\n    let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);\n    try {\n      let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();\n      // let deploymentBlockNumber = await this.queryDeploymentBlockNumber(\n      //   poolAddress\n      // );\n      let toBlockAsNumber = await this.parseEndBlockTypeWhenRecover(\n        latestEventBlockNumber,\n        toBlock,\n        poolAddress\n      );\n      // if (toBlockAsNumber < deploymentBlockNumber)\n      //   throw new Error(\"toBlock is too small, the pool hasn't been deployed.\");\n\n      if (toBlockAsNumber < latestEventBlockNumber) {\n        logger.info(\"It's already up to date.\");\n        return;\n      }\n\n      let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);\n\n      // check and record initialize event if needed\n      let updateInitializationEvent = false;\n      let initializationEventBlockNumber =\n        await eventDB.getInitializationEventBlockNumber();\n      if (0 == initializationEventBlockNumber) {\n        updateInitializationEvent = true;\n        let initializeTopic = uniswapV3Pool.filters.Initialize();\n        let initializationEvent = await this.queryFilterWithRetry(\n          uniswapV3Pool,\n          initializeTopic\n        );\n        await eventDB.addInitialSqrtPriceX96(\n          initializationEvent[0].args.sqrtPriceX96.toString()\n        );\n        initializationEventBlockNumber = initializationEvent[0].blockNumber;\n        await eventDB.saveInitializationEventBlockNumber(\n          initializationEventBlockNumber\n        );\n        await eventDB.saveLatestEventBlockNumber(\n          initializationEventBlockNumber\n        );\n      }\n\n      if (\n        !updateInitializationEvent &&\n        toBlockAsNumber == latestEventBlockNumber\n      ) {\n        logger.info(\"It's already up to date.\");\n        return;\n      }\n\n      let fromBlockAsNumber = updateInitializationEvent\n        ? initializationEventBlockNumber\n        : latestEventBlockNumber + 1;\n\n      // remove incomplete events\n      await eventDB.deleteLiquidityEventsByBlockNumber(\n        EventType.MINT,\n        fromBlockAsNumber,\n        toBlockAsNumber\n      );\n      await eventDB.deleteLiquidityEventsByBlockNumber(\n        EventType.BURN,\n        fromBlockAsNumber,\n        toBlockAsNumber\n      );\n      await eventDB.deleteSwapEventsByBlockNumber(\n        fromBlockAsNumber,\n        toBlockAsNumber\n      );\n\n      // download events after initialization\n      let poolConfig = await eventDB.getPoolConfig();\n\n      if (this.eventDataSourceType === EventDataSourceType.SUBGRAPH) {\n        await this.downloadEventsFromSubgraph(\n          uniswapV3Pool,\n          poolAddress.toLowerCase(),\n          poolConfig!.token0,\n          poolConfig!.token1,\n          eventDB,\n          fromBlockAsNumber,\n          toBlockAsNumber,\n          batchSize\n        );\n      } else if (this.eventDataSourceType === EventDataSourceType.RPC) {\n        await this.downloadEventsFromRPC(\n          uniswapV3Pool,\n          eventDB,\n          fromBlockAsNumber,\n          toBlockAsNumber,\n          batchSize\n        );\n      }\n\n      // await this.preProcessEvents(poolAddress, eventDB);\n    } finally {\n      await eventDB.close();\n    }\n  }\n\n  async preProcessEvents(poolAddress: string, eventDB: EventDBManager) {\n    // let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();\n\n    // for verify burn events\n    // (deprecated)workaround for the bug of burn events, not working though\n    // await this.fixBurnEvents(poolAddress, eventDB, latestEventBlockNumber);\n\n    // for verify swap events\n    await this.preProcessSwapEvent(eventDB);\n\n    logger.info(\"Events have been pre-processed successfully.\");\n  }\n\n  async fixBurnEvents(\n    poolAddress: string,\n    eventDB: EventDBManager,\n    endBlock: number\n  ): Promise<void> {\n    let startBlock = await eventDB.getLatestVerifiedBurnBlockNumber();\n    if (startBlock == 0) {\n      let initializationEventBlockNumber =\n        await eventDB.getInitializationEventBlockNumber();\n      startBlock = initializationEventBlockNumber;\n    }\n\n    if (startBlock >= endBlock) {\n      logger.info(\"No burn events to fix.\");\n      return;\n    }\n\n    let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);\n\n    await this.fixBurnEventsFromRPC(\n      uniswapV3Pool,\n      eventDB,\n      startBlock,\n      endBlock,\n      10000\n    );\n  }\n\n  async initializeAndReplayEvents(\n    eventDB: EventDBManager,\n    configurableCorePool: ConfigurableCorePool,\n    endBlock: number,\n    onlyInitialize: boolean = false\n  ): Promise<ConfigurableCorePool> {\n    let initializationEventBlockNumber =\n      await eventDB.getInitializationEventBlockNumber();\n\n    let initialSqrtPriceX96 = await eventDB.getInitialSqrtPriceX96();\n    await configurableCorePool.initialize(initialSqrtPriceX96);\n\n    if (onlyInitialize) return configurableCorePool;\n\n    // replay events to find swap input param we need\n    let startBlock = initializationEventBlockNumber;\n    let currBlock = startBlock;\n\n    let startTime = Date.now();\n\n    while (currBlock <= endBlock) {\n      let nextEndBlock =\n        this.nextBatch(currBlock) > endBlock\n          ? endBlock\n          : this.nextBatch(currBlock);\n      let events = await this.getAndSortEventByBlock(\n        eventDB,\n        currBlock,\n        nextEndBlock\n      );\n      if (events.length > 0) {\n        await this.replayEventsAndAssertReturnValues(\n          eventDB,\n          configurableCorePool,\n          events\n        );\n\n        await eventDB.saveLatestVerifiedSwapBlockNumber(nextEndBlock);\n\n        let endTime = Date.now();\n        let duration = endTime - startTime;\n        logger.info(\n          `Replay events duration: ${duration}ms, events: ${\n            events.length\n          }, average duration per event: ${\n            duration / events.length\n          }ms, fromBlock: ${currBlock}, toBlock: ${nextEndBlock}`\n        );\n        startTime = endTime;\n      }\n      currBlock = nextEndBlock + 1;\n    }\n    return configurableCorePool;\n  }\n\n  private async downloadEventsFromSubgraph(\n    uniswapV3Pool: UniswapV3Pool,\n    poolAddress: string,\n    token0: string,\n    token1: string,\n    eventDB: EventDBManager,\n    fromBlock: number,\n    toBlock: number,\n    batchSize: number,\n    liquidityEventsOnly: boolean = false\n  ) {\n    while (fromBlock <= toBlock) {\n      let endBlock =\n        fromBlock + batchSize > toBlock ? toBlock : fromBlock + batchSize;\n      let latestEventBlockNumber = Math.max(\n        fromBlock,\n        // the quality of burn events from RPC is far better than from subgraph, so we use RPC to get mint and burn events\n        await this.saveEventsFromRPC(\n          uniswapV3Pool,\n          eventDB,\n          EventType.MINT,\n          fromBlock,\n          endBlock\n        ),\n        await this.saveEventsFromRPC(\n          uniswapV3Pool,\n          eventDB,\n          EventType.BURN,\n          fromBlock,\n          endBlock\n        ),\n        liquidityEventsOnly\n          ? 0\n          : await this.saveEventsFromSubgraph(\n              poolAddress,\n              token0,\n              token1,\n              eventDB,\n              EventType.SWAP,\n              fromBlock,\n              endBlock\n            )\n      );\n      logger.info(\n        `latestEventBlockNumber: ${latestEventBlockNumber}, fromBlock: ${fromBlock}, toBlock: ${toBlock}, batchSize: ${batchSize}`\n      );\n      if (!liquidityEventsOnly)\n        await eventDB.saveLatestEventBlockNumber(latestEventBlockNumber);\n      fromBlock += batchSize + 1;\n    }\n    logger.info(\n      \"Events have been downloaded successfully. Please wait for pre-process to be done...\"\n    );\n  }\n\n  private async fixBurnEventsFromRPC(\n    uniswapV3Pool: UniswapV3Pool,\n    eventDB: EventDBManager,\n    fromBlock: number,\n    toBlock: number,\n    batchSize: number\n  ) {\n    while (fromBlock <= toBlock) {\n      let endBlock =\n        fromBlock + batchSize > toBlock ? toBlock : fromBlock + batchSize;\n      let events = await this.pullBurnEventsFromRPC(\n        uniswapV3Pool,\n        fromBlock,\n        endBlock\n      );\n      if (events.length > 0) {\n        for (let event of events) {\n          // logger.info(\n          //   `Fix burn event: ${event.transactionHash}, logIndex: ${\n          //     event.logIndex\n          //   }, amount: ${event.args.amount.toString()}, tickLower: ${\n          //     event.args.tickLower\n          //   }, tickUpper: ${event.args.tickUpper}`\n          // );\n          let burnEvents = await eventDB.getBurnEventsByTransactionHash(\n            event.transactionHash\n          );\n          if (burnEvents.length == 0) {\n            logger.warn(\n              `Burn event: ${event.transactionHash}, logIndex: ${\n                event.logIndex\n              }, amount: ${event.args.amount.toString()}, tickLower: ${\n                event.args.tickLower\n              }, tickUpper: ${event.args.tickUpper} not found`\n            );\n            continue;\n          } else if (burnEvents.length > 1) {\n            logger.warn(\n              `Burn event: ${event.transactionHash}, logIndex: ${\n                event.logIndex\n              }, amount: ${event.args.amount.toString()}, tickLower: ${\n                event.args.tickLower\n              }, tickUpper: ${event.args.tickUpper} found multiple times: ${\n                burnEvents.length\n              }`\n            );\n\n            await eventDB.saveBurnEvent(\n              event.transactionHash,\n              event.logIndex,\n              event.args.amount.toString(),\n              event.args.amount0.toString(),\n              event.args.amount1.toString(),\n              event.args.tickLower,\n              event.args.tickUpper\n            );\n          }\n        }\n        await eventDB.saveLatestVerifiedBurnBlockNumber(\n          events[events.length - 1].blockNumber\n        );\n      }\n      fromBlock += batchSize + 1;\n    }\n    logger.info(\"Burn events have been fixed successfully.\");\n  }\n\n  private async downloadEventsFromRPC(\n    uniswapV3Pool: UniswapV3Pool,\n    eventDB: EventDBManager,\n    fromBlock: number,\n    toBlock: number,\n    batchSize: number\n  ) {\n    while (fromBlock <= toBlock) {\n      let endBlock =\n        fromBlock + batchSize > toBlock ? toBlock : fromBlock + batchSize;\n      let latestEventBlockNumber = Math.max(\n        await this.saveEventsFromRPC(\n          uniswapV3Pool,\n          eventDB,\n          EventType.MINT,\n          fromBlock,\n          endBlock\n        ),\n        await this.saveEventsFromRPC(\n          uniswapV3Pool,\n          eventDB,\n          EventType.BURN,\n          fromBlock,\n          endBlock\n        ),\n        await this.saveEventsFromRPC(\n          uniswapV3Pool,\n          eventDB,\n          EventType.SWAP,\n          fromBlock,\n          endBlock\n        )\n      );\n      await eventDB.saveLatestEventBlockNumber(latestEventBlockNumber);\n      fromBlock += batchSize + 1;\n    }\n    logger.info(\n      \"Events have been downloaded successfully. Please wait for pre-process to be done...\"\n    );\n  }\n\n  private async queryFilterWithRetry(\n    contract: any,\n    filter: any,\n    fromBlock?: number,\n    toBlock?: number\n  ): Promise<any[]> {\n    const maxRetries = 5;\n    const baseDelay = 2000; // 2 seconds\n    const maxDelay = 30000; // 30 seconds\n\n    for (let attempt = 1; attempt <= maxRetries; attempt++) {\n      try {\n        logger.info(\n          `RPC queryFilter attempt ${attempt}/${maxRetries} (${fromBlock}-${toBlock})`\n        );\n\n        const events = await contract.queryFilter(filter, fromBlock, toBlock);\n\n        logger.info(\n          `RPC queryFilter successful on attempt ${attempt}, found ${events.length} events`\n        );\n        return events;\n      } catch (error: any) {\n        logger.error(\n          `RPC queryFilter attempt ${attempt} failed:`,\n          error.message\n        );\n\n        // if it is the last attempt, throw an error\n        if (attempt === maxRetries) {\n          logger.error(\n            `All ${maxRetries} RPC queryFilter attempts failed. Last error:`,\n            error.message\n          );\n          throw new Error(\n            `RPC queryFilter failed after ${maxRetries} attempts: ${error.message}`\n          );\n        }\n\n        // calculate the delay time (exponential backoff)\n        const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);\n        logger.info(`Waiting ${delay}ms before retry...`);\n\n        // wait and retry\n        await new Promise((resolve) => setTimeout(resolve, delay));\n      }\n    }\n\n    // this should not be reached, but for type safety\n    throw new Error(\"RPC queryFilter failed after all retries\");\n  }\n\n  private async request(query: string) {\n    const maxRetries = 10;\n    const baseDelay = 1000; // 1 second\n    const maxDelay = 60000; // 1 minute\n\n    // proxy configuration - can be set through environment variables\n    const proxyConfig = {\n      host: process.env.HTTP_PROXY_HOST || process.env.HTTPS_PROXY_HOST,\n      port: parseInt(\n        process.env.HTTP_PROXY_PORT || process.env.HTTPS_PROXY_PORT || \"0\"\n      ),\n      auth: process.env.HTTP_PROXY_AUTH || process.env.HTTPS_PROXY_AUTH,\n    };\n\n    // check if proxy is needed\n    const useProxy = proxyConfig.host && proxyConfig.port > 0;\n\n    // save original environment variables\n    const originalHttpProxy = process.env.HTTP_PROXY;\n    const originalHttpsProxy = process.env.HTTPS_PROXY;\n    const originalHttpProxyAuth = process.env.HTTP_PROXY_AUTH;\n    const originalHttpsProxyAuth = process.env.HTTPS_PROXY_AUTH;\n\n    try {\n      for (let attempt = 1; attempt <= maxRetries; attempt++) {\n        try {\n          logger.info(`GraphQL request attempt ${attempt}/${maxRetries}`);\n\n          // if proxy is configured, add proxy settings\n          if (useProxy) {\n            process.env.HTTP_PROXY = `http://${proxyConfig.host}:${proxyConfig.port}`;\n            process.env.HTTPS_PROXY = `http://${proxyConfig.host}:${proxyConfig.port}`;\n            if (proxyConfig.auth) {\n              process.env.HTTP_PROXY_AUTH = proxyConfig.auth;\n              process.env.HTTPS_PROXY_AUTH = proxyConfig.auth;\n            }\n            logger.info(`Using proxy: ${proxyConfig.host}:${proxyConfig.port}`);\n          }\n\n          const response = await request(\n            BSC_PANCAKE_V3_SUBGRAPH_ENDPOINT,\n            query,\n            {},\n            headers\n          );\n\n          // check if the response is valid\n          if (!response) {\n            throw new Error(\"Empty response received\");\n          }\n\n          // check GraphQL errors\n          if (response.errors && response.errors.length > 0) {\n            const errorMessages = response.errors\n              .map((err: any) => err.message)\n              .join(\", \");\n            throw new Error(`GraphQL errors: ${errorMessages}`);\n          }\n\n          logger.info(`GraphQL request successful on attempt ${attempt}`);\n          return response;\n        } catch (error: any) {\n          logger.error(`Request attempt ${attempt} failed:`, error.message);\n\n          // if it is the last attempt, throw an error\n          if (attempt === maxRetries) {\n            logger.error(\n              `All ${maxRetries} attempts failed. Last error:`,\n              error.message\n            );\n            throw new Error(\n              `GraphQL request failed after ${maxRetries} attempts: ${error.message}`\n            );\n          }\n\n          // calculate the delay time (exponential backoff)\n          const delay = Math.min(\n            baseDelay * Math.pow(2, attempt - 1),\n            maxDelay\n          );\n          logger.info(`Waiting ${delay}ms before retry...`);\n\n          // wait and retry\n          await new Promise((resolve) => setTimeout(resolve, delay));\n        }\n      }\n    } finally {\n      // restore original environment variables\n      if (originalHttpProxy !== undefined) {\n        process.env.HTTP_PROXY = originalHttpProxy;\n      } else {\n        delete process.env.HTTP_PROXY;\n      }\n      if (originalHttpsProxy !== undefined) {\n        process.env.HTTPS_PROXY = originalHttpsProxy;\n      } else {\n        delete process.env.HTTPS_PROXY;\n      }\n      if (originalHttpProxyAuth !== undefined) {\n        process.env.HTTP_PROXY_AUTH = originalHttpProxyAuth;\n      } else {\n        delete process.env.HTTP_PROXY_AUTH;\n      }\n      if (originalHttpsProxyAuth !== undefined) {\n        process.env.HTTPS_PROXY_AUTH = originalHttpsProxyAuth;\n      } else {\n        delete process.env.HTTPS_PROXY_AUTH;\n      }\n    }\n  }\n\n  private async saveEventsFromSubgraph(\n    poolAddress: string,\n    token0: string,\n    token1: string,\n    eventDB: EventDBManager,\n    eventType: EventType,\n    fromBlock: number,\n    toBlock: number\n  ): Promise<number> {\n    // let token0Decimals = tokens[0].decimals;\n    // let token1Decimals = tokens[1].decimals;\n    // let fromTimestamp = (await this.RPCProvider.getBlock(fromBlock)).timestamp;\n    // let toTimestamp = (await this.RPCProvider.getBlock(toBlock)).timestamp;\n    logger.info(\"--------------------------------\");\n    logger.info(poolAddress, token0, token1, eventType);\n    logger.info(fromBlock, toBlock);\n    logger.info(\"--------------------------------\");\n    let latestEventBlockNumber = fromBlock;\n    let skip = 0;\n    while (true) {\n      if (eventType === EventType.MINT) {\n        const query = gql`\n          query {\n            liquidityPool(id: \"${poolAddress}\") {\n              deposits(\n                first: 1000\n                skip: ${skip}\n                where: { blockNumber_gte: ${fromBlock}, blockNumber_lte: ${toBlock} }\n                orderBy: blockNumber\n                orderDirection: asc\n              ) {\n                account {\n                  id\n                }\n                blockNumber\n                hash\n                inputTokenAmounts\n                liquidity\n                logIndex\n                tickLower\n                tickUpper\n                timestamp\n              }\n            }\n          }\n        `;\n\n        logger.info(\"mint query: \", query);\n\n        let data = await this.request(query);\n\n        if (null == data.liquidityPool) {\n          latestEventBlockNumber = toBlock;\n          break;\n        }\n\n        let events = data.liquidityPool.deposits;\n\n        if (events.length > 0) {\n          const liquidityEvents = events.map((event) => {\n            let date = new Date(event.timestamp * 1000);\n            return {\n              type: eventType,\n              msg_sender: event.account.id,\n              recipient: \"\",\n              liquidity: convertTokenStr(event.liquidity),\n              amount0: convertTokenStr(event.inputTokenAmounts[0]),\n              amount1: convertTokenStr(event.inputTokenAmounts[1]),\n              tick_lower: event.tickLower,\n              tick_upper: event.tickUpper,\n              block_number: event.blockNumber,\n              transaction_hash: event.hash,\n              log_index: event.logIndex,\n              date: date,\n            };\n          });\n\n          await eventDB.batchInsertLiquidityEvents(liquidityEvents);\n          latestEventBlockNumber = events[events.length - 1].blockNumber;\n        }\n        if (events.length < 1000) {\n          break;\n        } else {\n          skip += 1000;\n        }\n      } else if (eventType === EventType.BURN) {\n        const query = gql`\n          query {\n            liquidityPool(id: \"${poolAddress}\") {\n              withdraws(\n                first: 1000\n                skip: ${skip}\n                where: { blockNumber_gte: ${fromBlock}, blockNumber_lte: ${toBlock} }\n                orderBy: blockNumber\n                orderDirection: asc\n              ) {\n                account {\n                  id\n                }\n                blockNumber\n                hash\n                inputTokenAmounts\n                liquidity\n                logIndex\n                tickLower\n                tickUpper\n                timestamp\n              }\n            }\n          }\n        `;\n\n        logger.info(\"burn query: \", query);\n\n        let data = await this.request(query);\n\n        if (null == data.liquidityPool) {\n          latestEventBlockNumber = toBlock;\n          break;\n        }\n\n        let events = data.liquidityPool.withdraws;\n\n        if (events.length > 0) {\n          const liquidityEvents = events.map((event) => {\n            let date = new Date(event.timestamp * 1000);\n            return {\n              type: eventType,\n              msg_sender: event.account.id,\n              recipient: \"\",\n              liquidity: convertTokenStr(event.liquidity),\n              amount0: convertTokenStr(event.inputTokenAmounts[0]),\n              amount1: convertTokenStr(event.inputTokenAmounts[1]),\n              tick_lower: event.tickLower,\n              tick_upper: event.tickUpper,\n              block_number: event.blockNumber,\n              transaction_hash: event.hash,\n              log_index: event.logIndex,\n              date: date,\n            };\n          });\n\n          await eventDB.batchInsertLiquidityEvents(liquidityEvents);\n          latestEventBlockNumber = events[events.length - 1].blockNumber;\n        }\n        if (events.length < 1000) {\n          break;\n        } else {\n          skip += 1000;\n        }\n      } else if (eventType === EventType.SWAP) {\n        const query = gql`\n          query {\n            liquidityPool(id: \"${poolAddress}\") {\n              swaps(\n                first: 1000\n                skip: ${skip}\n                where: { blockNumber_gte: ${fromBlock}, blockNumber_lte: ${toBlock} }\n                orderBy: blockNumber\n                orderDirection: asc\n              ) {\n                amountIn\n                amountOut\n                blockNumber\n                hash\n                logIndex\n                nonce\n                tick\n                timestamp\n                account {\n                  id\n                }\n                tokenIn {\n                  id\n                }\n                tokenOut {\n                  id\n                }\n              }\n            }\n          }\n        `;\n\n        logger.info(\"swap query: \", query);\n\n        let data = await this.request(query);\n\n        if (\n          null == data.liquidityPool ||\n          data.liquidityPool.swaps.length == 0\n        ) {\n          latestEventBlockNumber = toBlock;\n          break;\n        }\n\n        let events = data.liquidityPool.swaps;\n\n        if (events.length > 0) {\n          const swapEvents = events.map((event) => {\n            let amount0;\n            let amount1;\n            if (token0.toLowerCase() === event.tokenIn.id.toLowerCase()) {\n              amount0 = event.amountIn;\n              amount1 = -event.amountOut;\n            } else {\n              amount0 = -event.amountOut;\n              amount1 = event.amountIn;\n            }\n\n            let date = new Date(event.timestamp * 1000);\n            return {\n              msg_sender: event.account.id,\n              recipient: event.account.id,\n              amount0: convertTokenStr(amount0),\n              amount1: convertTokenStr(amount1),\n              sqrt_price_x96: \"-1\",\n              liquidity: \"-1\",\n              tick: event.tick,\n              block_number: event.blockNumber,\n              transaction_hash: event.hash,\n              log_index: event.logIndex,\n              date: date,\n            };\n          });\n\n          await eventDB.batchInsertSwapEvents(swapEvents);\n          latestEventBlockNumber = events[events.length - 1].blockNumber;\n        }\n        if (events.length < 1000) {\n          break;\n        } else {\n          skip += 1000;\n        }\n      }\n    }\n    return latestEventBlockNumber;\n  }\n\n  private async pullBurnEventsFromRPC(\n    uniswapV3Pool: UniswapV3Pool,\n    fromBlock: number,\n    toBlock: number\n  ): Promise<\n    TypedEvent<\n      [string, number, number, BigNumber, BigNumber, BigNumber] & {\n        owner: string;\n        tickLower: number;\n        tickUpper: number;\n        amount: BigNumber;\n        amount0: BigNumber;\n        amount1: BigNumber;\n      }\n    >[]\n  > {\n    let topic = uniswapV3Pool.filters.Burn();\n    let events = await this.queryFilterWithRetry(\n      uniswapV3Pool,\n      topic,\n      fromBlock,\n      toBlock\n    );\n\n    return events;\n  }\n\n  private async saveEventsFromRPC(\n    uniswapV3Pool: UniswapV3Pool,\n    eventDB: EventDBManager,\n    eventType: EventType,\n    fromBlock: number,\n    toBlock: number\n  ): Promise<number> {\n    let latestEventBlockNumber = fromBlock;\n    if (eventType === EventType.MINT) {\n      let topic = uniswapV3Pool.filters.Mint();\n      let events = await this.queryFilterWithRetry(\n        uniswapV3Pool,\n        topic,\n        fromBlock,\n        toBlock\n      );\n\n      if (events.length > 0) {\n        const liquidityEvents = await Promise.all(\n          events.map(async (event) => {\n            // to get better efficiency, we use local timestamp(inserted time) instead of block timestamp\n            return {\n              type: eventType,\n              msg_sender: event.args.owner,\n              recipient: \"\",\n              liquidity: event.args.amount.toString(),\n              amount0: event.args.amount0.toString(),\n              amount1: event.args.amount1.toString(),\n              tick_lower: event.args.tickLower,\n              tick_upper: event.args.tickUpper,\n              block_number: event.blockNumber,\n              transaction_hash: event.transactionHash,\n              log_index: event.logIndex,\n              date: new Date(),\n            };\n          })\n        );\n\n        await eventDB.batchInsertLiquidityEvents(liquidityEvents);\n        latestEventBlockNumber = Math.max(...events.map((e) => e.blockNumber));\n      }\n    } else if (eventType === EventType.BURN) {\n      let topic = uniswapV3Pool.filters.Burn();\n      let events = await this.queryFilterWithRetry(\n        uniswapV3Pool,\n        topic,\n        fromBlock,\n        toBlock\n      );\n\n      if (events.length > 0) {\n        const liquidityEvents = await Promise.all(\n          events.map(async (event) => {\n            return {\n              type: eventType,\n              msg_sender: event.args.owner,\n              recipient: \"\",\n              liquidity: event.args.amount.toString(),\n              amount0: event.args.amount0.toString(),\n              amount1: event.args.amount1.toString(),\n              tick_lower: event.args.tickLower,\n              tick_upper: event.args.tickUpper,\n              block_number: event.blockNumber,\n              transaction_hash: event.transactionHash,\n              log_index: event.logIndex,\n              date: new Date(),\n            };\n          })\n        );\n\n        await eventDB.batchInsertLiquidityEvents(liquidityEvents);\n        latestEventBlockNumber = Math.max(...events.map((e) => e.blockNumber));\n      }\n    } else if (eventType === EventType.SWAP) {\n      let topic = uniswapV3Pool.filters.Swap();\n      let events = await this.queryFilterWithRetry(\n        uniswapV3Pool,\n        topic,\n        fromBlock,\n        toBlock\n      );\n\n      if (events.length > 0) {\n        const swapEvents = await Promise.all(\n          events.map(async (event) => {\n            return {\n              msg_sender: event.args.sender,\n              recipient: event.args.recipient,\n              amount0: event.args.amount0.toString(),\n              amount1: event.args.amount1.toString(),\n              sqrt_price_x96: event.args.sqrtPriceX96.toString(),\n              liquidity: event.args.liquidity.toString(),\n              tick: event.args.tick,\n              block_number: event.blockNumber,\n              transaction_hash: event.transactionHash,\n              log_index: event.logIndex,\n              date: new Date(),\n            };\n          })\n        );\n\n        await eventDB.batchInsertSwapEvents(swapEvents);\n        latestEventBlockNumber = Math.max(...events.map((e) => e.blockNumber));\n      }\n    }\n    return latestEventBlockNumber;\n  }\n\n  async preProcessSwapEvent(eventDB: EventDBManager) {\n    let latestVerifiedSwapBlockNumber =\n      await eventDB.getLatestVerifiedSwapBlockNumber();\n    if (latestVerifiedSwapBlockNumber == 0) {\n      latestVerifiedSwapBlockNumber =\n        await eventDB.getInitializationEventBlockNumber();\n    }\n\n    let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();\n    if (latestVerifiedSwapBlockNumber >= latestEventBlockNumber) {\n      logger.info(\"No swap events to pre-process.\");\n      return;\n    }\n\n    // initialize configurableCorePool\n    let simulatorDBManager: SimulationDataManager =\n      await SQLiteSimulationDataManager.buildInstance();\n    let poolConfig = await eventDB.getPoolConfig();\n    let configurableCorePool: ConfigurableCorePool =\n      new ConfigurableCorePoolImpl(\n        new PoolState(poolConfig),\n        new SimulatorRoadmapManager(simulatorDBManager),\n        new SimulatorConsoleVisitor(),\n        new SimulatorPersistenceVisitor(simulatorDBManager)\n      );\n    await this.initializeAndReplayEvents(\n      eventDB,\n      configurableCorePool,\n      await eventDB.getLatestEventBlockNumber()\n    );\n    await simulatorDBManager.close();\n    logger.info(\"Events have been pre-processed successfully.\");\n  }\n\n  private nextBatch(currBlock: number) {\n    // we take an hour as step length, consider block interval as 0.75s then 60 * 60 / 0.75 = 4800\n    return currBlock + 4800;\n  }\n\n  private async getCorePoolContarct(\n    poolAddress: string\n  ): Promise<UniswapV3Pool> {\n    return UniswapV3PoolFactory.connect(poolAddress, this.RPCProvider);\n  }\n\n  private async getAndSortEventByBlock(\n    eventDB: EventDBManager,\n    startBlock: number,\n    endBlock: number\n  ): Promise<(LiquidityEvent | SwapEvent)[]> {\n    let events: (LiquidityEvent | SwapEvent)[] = [];\n    let mintEvents: LiquidityEvent[] =\n      await eventDB.getLiquidityEventsByBlockNumber(\n        EventType.MINT,\n        startBlock,\n        endBlock\n      );\n    let burnEvents: LiquidityEvent[] =\n      await eventDB.getLiquidityEventsByBlockNumber(\n        EventType.BURN,\n        startBlock,\n        endBlock\n      );\n    let swapEvents: SwapEvent[] = await eventDB.getSwapEventsByBlockNumber(\n      startBlock,\n      endBlock\n    );\n    events.push(...mintEvents);\n    events.push(...burnEvents);\n    events.push(...swapEvents);\n    events.sort(function (a, b) {\n      return a.blockNumber == b.blockNumber\n        ? a.logIndex - b.logIndex\n        : a.blockNumber - b.blockNumber;\n    });\n    return events;\n  }\n\n  private async replayEventsAndAssertReturnValues(\n    eventDB: EventDBManager,\n    configurableCorePool: ConfigurableCorePool,\n    paramArr: (LiquidityEvent | SwapEvent)[]\n  ): Promise<void> {\n    for (let index = 0; index < paramArr.length; index++) {\n      // avoid stack overflow\n      if (index % 4000 == 0) {\n        configurableCorePool.takeSnapshot(\"\");\n      }\n\n      let param = paramArr[index];\n      let amount0: JSBI, amount1: JSBI;\n      switch (param.type) {\n        case EventType.MINT:\n          ({ amount0, amount1 } = await configurableCorePool.mint(\n            param.msgSender,\n            param.tickLower,\n            param.tickUpper,\n            param.liquidity\n          ));\n          if (\n            JSBI.notEqual(amount0, param.amount0) ||\n            JSBI.notEqual(amount1, param.amount1)\n          )\n            logger.warn(\n              `Mint result is not correct. We need to endure this error. Result: amount0: ${amount0}, amount1: ${amount1}. Event: ${printParams(\n                param\n              )}.`\n            );\n          logger.debug(\n            `Mint success. Event txn hash: ${param.transactionHash}, log_index: ${param.logIndex}.`\n          );\n          break;\n        case EventType.BURN:\n          // let positionManager =\n          //   configurableCorePool.getCorePool().positionManager;\n          // let positions = positionManager.getPositionsByOwner(param.msgSender);\n          // if (positions.size == 0) {\n          //   logger.warn(\n          //     `Burn failed. No position found. We need to endure this error. Event: ${printParams(\n          //       param\n          //     )}.`\n          //   );\n          //   continue;\n          // }\n\n          // for (let [key, position] of positions.entries()) {\n          //   let { owner, tickLower, tickUpper } =\n          //     PositionManager.extractFromKey(key);\n          //   ({ amount0, amount1 } = await configurableCorePool.burn(\n          //     owner,\n          //     tickLower,\n          //     tickUpper,\n          //     position.liquidity\n          //   ));\n          // }\n\n          if (param.tickLower == null || param.tickUpper == null) {\n            logger.warn(\n              `Burn failed. TickLower or TickUpper is null. We need to endure this error. Event: ${printParams(\n                param\n              )}.`\n            );\n            continue;\n          }\n\n          ({ amount0, amount1 } = await configurableCorePool.burn(\n            param.msgSender,\n            param.tickLower,\n            param.tickUpper,\n            param.liquidity\n          ));\n          if (\n            JSBI.notEqual(amount0, param.amount0) ||\n            JSBI.notEqual(amount1, param.amount1)\n          )\n            logger.warn(\n              `Burn result is not correct. We need to endure this error. Result: amount0: ${amount0}, amount1: ${amount1}. Event: ${printParams(\n                param\n              )}.`\n            );\n          logger.debug(\n            `Burn success. Event txn hash: ${param.transactionHash}, log_index: ${param.logIndex}.`\n          );\n          break;\n        case EventType.SWAP:\n          // try-error to find `amountSpecified` and `sqrtPriceLimitX96` to resolve to the same result as swap event records\n          try {\n            // logger.info(\n            //   `try to resolve swap event, event txn hash: ${param.transactionHash}, log_index: ${param.logIndex}`\n            // );\n            // let { amountSpecified, sqrtPriceX96 } =\n            //   await configurableCorePool.resolveInputFromSwapResultEvent(param);\n            // logger.info(\n            //   `amountSpecified: ${amountSpecified}, sqrtPriceX96: ${sqrtPriceX96}`\n            // );\n\n            let zeroForOne: boolean = JSBI.greaterThan(param.amount0, ZERO)\n              ? true\n              : false;\n            let amountSpecified = param.amount0;\n\n            await configurableCorePool.swap(\n              zeroForOne,\n              amountSpecified,\n              undefined\n            );\n            // add AmountSpecified column to swap event if we need to\n            // if (ZERO == param.amountSpecified) {\n            //   await eventDB.addAmountSpecified(\n            //     param.id,\n            //     amountSpecified.toString()\n            //   );\n            // }\n          } catch (error) {\n            return Promise.reject(`Swap failed. Event: ${printParams(param)}.`);\n          }\n          logger.debug(`Swap success. Event: ${printParams(param)}.`);\n          break;\n        default:\n          // @ts-ignore: ExhaustiveCheck\n          const exhaustiveCheck: never = param;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/client/SimulatorClient.ts",
    "content": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { PoolState } from \"../model/PoolState\";\nimport { PoolConfig } from \"../model/PoolConfig\";\nimport { SimulationDataManager } from \"../interface/SimulationDataManager\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { SimulatorRoadmapManager } from \"../manager/SimulatorRoadmapManager\";\nimport { SnapshotProfile } from \"../entity/SnapshotProfile\";\nimport { SimulatorRoadmapManager as ISimulatorRoadmapManager } from \"../interface/SimulatorRoadmapManager\";\nimport { ConfigurableCorePool as IConfigurableCorePool } from \"../interface/ConfigurableCorePool\";\nimport { SimulatorConsoleVisitor } from \"../manager/SimulatorConsoleVisitor\";\nimport { SimulatorPersistenceVisitor } from \"../manager/SimulatorPersistenceVisitor\";\nimport { EventDBManager } from \"../manager/EventDBManager\";\nimport { BSCDataDownloader } from \"./BSCDataDownloader\";\nimport {\n  EndBlockTypeWhenInit,\n  EndBlockTypeWhenRecover,\n} from \"../entity/EndBlockType\";\nimport { EventDataSourceType } from \"../enum/EventDataSourceType\";\nimport { Tick } from \"../model/Tick\";\nimport { PoolStateHelper } from \"../util/PoolStateHelper\";\n\nexport class SimulatorClient {\n  private simulatorDBManager: SimulationDataManager;\n  private readonly _simulatorRoadmapManager: SimulatorRoadmapManager;\n\n  public get simulatorRoadmapManager(): ISimulatorRoadmapManager {\n    return this._simulatorRoadmapManager;\n  }\n\n  constructor(simulatorDBManager: SimulationDataManager) {\n    this.simulatorDBManager = simulatorDBManager;\n    this._simulatorRoadmapManager = new SimulatorRoadmapManager(\n      simulatorDBManager\n    );\n  }\n\n  // async initCorePoolFromMainnet(\n  //   poolName: string = \"\",\n  //   poolAddress: string,\n  //   endBlock: EndBlockTypeWhenInit,\n  //   RPCProviderUrl: string | undefined = undefined,\n  //   eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH\n  // ): Promise<IConfigurableCorePool> {\n  //   let mainnetDataDownloader: BSCDataDownloader = new BSCDataDownloader(\n  //     RPCProviderUrl,\n  //     eventDataSourceType\n  //   );\n  //   await mainnetDataDownloader.download(\n  //     poolName,\n  //     poolAddress,\n  //     deploymentBlockNumber,\n  //     endBlock\n  //   );\n  //   let eventDBFilePath = mainnetDataDownloader.generateMainnetEventDBFilePath(\n  //     poolName,\n  //     poolAddress\n  //   );\n  //   let eventDB = await EventDBManager.buildInstance(eventDBFilePath);\n  //   try {\n  //     let poolConfig = await eventDB.getPoolConfig();\n  //     let configurableCorePool: IConfigurableCorePool =\n  //       this.initCorePoolFromConfig(poolConfig!);\n  //     if (endBlock == \"afterDeployment\") return configurableCorePool;\n  //     let endBlockInNumber =\n  //       await mainnetDataDownloader.parseEndBlockTypeWhenInit(\n  //         endBlock,\n  //         poolAddress\n  //       );\n  //     await mainnetDataDownloader.initializeAndReplayEvents(\n  //       eventDB,\n  //       configurableCorePool,\n  //       endBlockInNumber,\n  //       endBlock == \"afterInitialization\"\n  //     );\n  //     return configurableCorePool;\n  //   } finally {\n  //     await eventDB.close();\n  //   }\n  // }\n\n  async recoverFromMainnetEventDBFile(\n    mainnetEventDBFilePath: string,\n    endBlock: EndBlockTypeWhenRecover,\n    RPCProviderUrl: string | undefined = undefined,\n    eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH\n  ): Promise<IConfigurableCorePool> {\n    let mainnetDataDownloader: BSCDataDownloader = new BSCDataDownloader(\n      RPCProviderUrl,\n      eventDataSourceType\n    );\n    await mainnetDataDownloader.update(mainnetEventDBFilePath, endBlock);\n    let { poolAddress } = mainnetDataDownloader.parseFromMainnetEventDBFilePath(\n      mainnetEventDBFilePath\n    );\n    let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);\n    try {\n      let poolConfig = await eventDB.getPoolConfig();\n      let configurableCorePool: IConfigurableCorePool =\n        this.initCorePoolFromConfig(poolConfig!);\n      if (endBlock == \"afterDeployment\") return configurableCorePool;\n      let endBlockInNumber =\n        await mainnetDataDownloader.parseEndBlockTypeWhenRecover(\n          await eventDB.getLatestEventBlockNumber(),\n          endBlock,\n          poolAddress\n        );\n      await mainnetDataDownloader.initializeAndReplayEvents(\n        eventDB,\n        configurableCorePool,\n        endBlockInNumber,\n        endBlock == \"afterInitialization\"\n      );\n      return configurableCorePool;\n    } finally {\n      await eventDB.close();\n    }\n  }\n\n  initCorePoolFromConfig(poolConfig: PoolConfig): IConfigurableCorePool {\n    return new ConfigurableCorePool(\n      new PoolState(poolConfig),\n      this._simulatorRoadmapManager,\n      new SimulatorConsoleVisitor(),\n      new SimulatorPersistenceVisitor(this.simulatorDBManager)\n    );\n  }\n\n  initCorePoolFromOnchainData(\n    poolConfig: PoolConfig,\n    sqrtPriceX96: bigint,\n    liquidity: bigint,\n    tickCurrent: number,\n    feeGrowthGlobal0X128: bigint,\n    feeGrowthGlobal1X128: bigint,\n    populatedTicks: Map<number, Tick>\n  ): IConfigurableCorePool {\n    return new ConfigurableCorePool(\n      new PoolState(poolConfig),\n      this._simulatorRoadmapManager,\n      new SimulatorConsoleVisitor(),\n      new SimulatorPersistenceVisitor(this.simulatorDBManager),\n      PoolStateHelper.buildCorePoolByOnchainData(\n        poolConfig,\n        sqrtPriceX96,\n        liquidity,\n        tickCurrent,\n        feeGrowthGlobal0X128,\n        feeGrowthGlobal1X128,\n        populatedTicks\n      )\n    );\n  }\n\n  recoverCorePoolFromSnapshot(\n    snapshotId: string\n  ): Promise<IConfigurableCorePool> {\n    return this.getSnapshot(snapshotId).then((snapshot: Snapshot | undefined) =>\n      !snapshot\n        ? Promise.reject(\"This snapshot doesn't exist!\")\n        : Promise.resolve(\n            new ConfigurableCorePool(\n              PoolState.from(snapshot),\n              this._simulatorRoadmapManager,\n              new SimulatorConsoleVisitor(),\n              new SimulatorPersistenceVisitor(this.simulatorDBManager)\n            )\n          )\n    );\n  }\n\n  listSnapshotProfiles(): Promise<SnapshotProfile[]> {\n    return this.simulatorDBManager.getSnapshotProfiles();\n  }\n\n  shutdown(): Promise<void> {\n    return this.simulatorDBManager.close();\n  }\n\n  private getSnapshot(snapshotId: string): Promise<Snapshot | undefined> {\n    return this.simulatorDBManager.getSnapshot(snapshotId);\n  }\n}\n"
  },
  {
    "path": "src/client/index.ts",
    "content": "export * from \"./SimulatorClient\";\nexport * from \"./BSCDataDownloader\";\n"
  },
  {
    "path": "src/config/TunerConfig.ts",
    "content": "import path from \"path\";\nimport * as fs from \"fs\";\n\nexport interface TunerConfig {\n  RPCProviderUrl: string;\n}\n\nexport function loadConfig(file?: string): TunerConfig {\n  let customJson = {};\n  const configFile = file || path.join(process.cwd(), \"tuner.config.js\");\n  try {\n    customJson = require(configFile);\n  } catch (e) {\n    if (fs.existsSync(configFile)) {\n      throw new Error(`Cannot read Tuner JSON: ${configFile}: ${e}`);\n    }\n  }\n  const defaultJson = require(path.join(\n    __dirname,\n    \"..\",\n    \"..\",\n    \"tuner.config.js\"\n  ));\n  const merged = mergeDeep(defaultJson, customJson);\n\n  return {\n    ...merged,\n  };\n}\n\nexport function mergeDeep(target: any, source: any) {\n  const isObject = (obj: any) => obj && typeof obj === \"object\";\n\n  if (!isObject(target) || !isObject(source)) {\n    return source;\n  }\n\n  Object.keys(source).forEach((key) => {\n    const targetValue = target[key];\n    const sourceValue = source[key];\n\n    if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {\n      target[key] = sourceValue; // Always use source key, if given\n    } else if (isObject(targetValue) && isObject(sourceValue)) {\n      target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue);\n    } else {\n      target[key] = sourceValue;\n    }\n  });\n\n  return target;\n}\n"
  },
  {
    "path": "src/core/ConfigurableCorePool.ts",
    "content": "import JSBI from \"jsbi\";\nimport { PoolState } from \"../model/PoolState\";\nimport { Record } from \"../entity/Record\";\nimport { CorePool } from \"./CorePool\";\nimport { Visitable } from \"../interface/Visitable\";\nimport { ActionType } from \"../enum/ActionType\";\nimport { PoolStateHelper } from \"../util/PoolStateHelper\";\nimport { IdGenerator } from \"../util/IdGenerator\";\nimport { Transition } from \"../model/Transition\";\nimport { SimulatorVisitor } from \"../interface/SimulatorVisitor\";\nimport { SimulatorConsoleVisitor } from \"../manager/SimulatorConsoleVisitor\";\nimport { SimulatorPersistenceVisitor } from \"../manager/SimulatorPersistenceVisitor\";\nimport {\n  MethodParams,\n  ReturnParams,\n  MintParams,\n  BurnParams,\n  SwapParams,\n  CollectParams,\n  GeneralReturnParams,\n  InitializeParams,\n} from \"../interface/ActionParams\";\nimport { SimulatorRoadmapManager } from \"../manager/SimulatorRoadmapManager\";\nimport { ConfigurableCorePool as IConfigurableCorePool } from \"../interface/ConfigurableCorePool\";\nimport { CorePoolView } from \"../interface/CorePoolView\";\nimport { PoolStateView } from \"../interface/PoolStateView\";\nimport { Transition as TransitionView } from \"../interface/Transition\";\nimport { SwapEvent } from \"../entity/SwapEvent\";\nimport { RAPID_POSITION_OWNER, ZERO } from \"../enum/InternalConstants\";\nimport { FullMath } from \"..\";\n\nexport class ConfigurableCorePool implements IConfigurableCorePool, Visitable {\n  readonly id: string;\n  private simulatorConsoleVisitor: SimulatorConsoleVisitor;\n  private simulatorPersistenceVisitor: SimulatorPersistenceVisitor;\n  private _poolState: PoolState;\n  private simulatorRoadmapManager: SimulatorRoadmapManager;\n  private corePool: CorePool;\n  private postProcessorCallback: (\n    configurableCorePool: IConfigurableCorePool,\n    transition: TransitionView\n  ) => Promise<void> = async function () {};\n\n  constructor(\n    poolState: PoolState,\n    simulatorRoadmapManager: SimulatorRoadmapManager,\n    simulatorConsoleVisitor: SimulatorConsoleVisitor,\n    simulatorPersistenceVisitor: SimulatorPersistenceVisitor,\n    corePool?: CorePool\n  ) {\n    this.simulatorConsoleVisitor = simulatorConsoleVisitor;\n    this.simulatorPersistenceVisitor = simulatorPersistenceVisitor;\n    this.id = IdGenerator.guid();\n    if (corePool) {\n      this.corePool = corePool;\n    } else {\n      if (poolState.hasSnapshot()) {\n        this.corePool = PoolStateHelper.buildCorePoolBySnapshot(\n          poolState.snapshot!\n        );\n      } else if (poolState.hasBaseSnapshot()) {\n        this.corePool = PoolStateHelper.buildCorePoolBySnapshot(\n          poolState.baseSnapshot!\n        );\n      } else {\n        this.corePool = PoolStateHelper.buildCorePoolByPoolConfig(\n          poolState.poolConfig\n        );\n      }\n    }\n    this._poolState = poolState;\n    this.simulatorRoadmapManager = simulatorRoadmapManager;\n    this.simulatorRoadmapManager.addPoolState(this.poolState);\n    this.simulatorRoadmapManager.addRoute(this);\n  }\n\n  getPoolState(): PoolStateView {\n    return this.poolState;\n  }\n\n  getCorePool(): CorePoolView {\n    return this.corePool;\n  }\n\n  initialize(\n    sqrtPriceX96: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<void> {\n    let currentPoolStateId = this.poolState.id;\n    try {\n      let res = this.corePool.initialize(sqrtPriceX96);\n      return this.postProcess(\n        ActionType.INITIALIZE,\n        { type: ActionType.INITIALIZE, sqrtPriceX96 } as InitializeParams,\n        {} as ReturnParams,\n        postProcessorCallback\n      ).then(() => Promise.resolve(res));\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n  }\n\n  setCoreGlobalState(sqrtPriceX96: JSBI, liquidity: JSBI, tickCurrent: number) {\n    this.corePool.setCoreGlobalState(sqrtPriceX96, liquidity, tickCurrent);\n  }\n\n  rapidMint(\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.rapidMint(tickLower, tickUpper, amount);\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    // TODO: rapid mint shoule be different from mint\n    return this.postProcess(\n      ActionType.MINT,\n      {\n        type: ActionType.MINT,\n        recipient: RAPID_POSITION_OWNER,\n        tickLower,\n        tickUpper,\n        amount,\n      } as MintParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  phantomMint(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.phantomMint(recipient, tickLower, tickUpper, amount);\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    // TODO: phantom mint shoule be different from mint\n    return this.postProcess(\n      ActionType.MINT,\n      {\n        type: ActionType.MINT,\n        recipient,\n        tickLower,\n        tickUpper,\n        amount,\n      } as MintParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  mint(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.mint(recipient, tickLower, tickUpper, amount);\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    return this.postProcess(\n      ActionType.MINT,\n      {\n        type: ActionType.MINT,\n        recipient,\n        tickLower,\n        tickUpper,\n        amount,\n      } as MintParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  rapidBurn(\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.rapidBurn(tickLower, tickUpper, amount);\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    // TODO: rapid burn shoule be different from burn\n    return this.postProcess(\n      ActionType.BURN,\n      {\n        type: ActionType.BURN,\n        owner: RAPID_POSITION_OWNER,\n        tickLower,\n        tickUpper,\n        amount,\n      } as BurnParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  phantomBurn(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.phantomBurn(owner, tickLower, tickUpper, amount);\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    // TODO: phantom burn shoule be different from burn\n    return this.postProcess(\n      ActionType.BURN,\n      {\n        type: ActionType.BURN,\n        owner,\n        tickLower,\n        tickUpper,\n        amount,\n      } as BurnParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  burn(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.burn(owner, tickLower, tickUpper, amount);\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    return this.postProcess(\n      ActionType.BURN,\n      {\n        type: ActionType.BURN,\n        owner,\n        tickLower,\n        tickUpper,\n        amount,\n      } as BurnParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  collect(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount0Requested: JSBI,\n    amount1Requested: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.collect(\n        recipient,\n        tickLower,\n        tickUpper,\n        amount0Requested,\n        amount1Requested\n      );\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    return this.postProcess(\n      ActionType.COLLECT,\n      {\n        type: ActionType.COLLECT,\n        recipient,\n        tickLower,\n        tickUpper,\n        amount0Requested,\n        amount1Requested,\n      } as CollectParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  swap(\n    zeroForOne: boolean,\n    amountSpecified: JSBI,\n    sqrtPriceLimitX96?: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }> {\n    let currentPoolStateId = this.poolState.id;\n    let res: GeneralReturnParams;\n    try {\n      res = this.corePool.swap(zeroForOne, amountSpecified, sqrtPriceLimitX96);\n    } catch (error) {\n      this.recover(currentPoolStateId);\n      throw error;\n    }\n    return this.postProcess(\n      ActionType.SWAP,\n      {\n        type: ActionType.SWAP,\n        zeroForOne,\n        amountSpecified,\n        sqrtPriceLimitX96,\n      } as SwapParams,\n      res,\n      postProcessorCallback\n    )\n      .then(() => Promise.resolve(res))\n      .catch((err) => {\n        this.recover(currentPoolStateId);\n        return Promise.reject(err);\n      });\n  }\n\n  querySwap(\n    zeroForOne: boolean,\n    amountSpecified: JSBI,\n    sqrtPriceLimitX96?: JSBI\n  ): Promise<{ amount0: JSBI; amount1: JSBI; sqrtPriceX96: JSBI }> {\n    return Promise.resolve(\n      this.corePool.querySwap(zeroForOne, amountSpecified, sqrtPriceLimitX96)\n    );\n  }\n\n  async resolveInputFromSwapResultEvent(\n    param: SwapEvent\n  ): Promise<{ amountSpecified: JSBI; sqrtPriceX96?: JSBI }> {\n    let tryWithDryRun = (\n      solutionIndex: number,\n      param: SwapEvent,\n      amountSpecified: JSBI,\n      sqrtPriceLimitX96?: JSBI\n    ) => {\n      let zeroForOne: boolean = JSBI.greaterThan(param.amount0, ZERO)\n        ? true\n        : false;\n      return this.querySwap(\n        zeroForOne,\n        amountSpecified,\n        sqrtPriceLimitX96\n      ).then(({ amount0, amount1, sqrtPriceX96 }) => {\n        // console.log(\n        //   `solutionIndex: ${solutionIndex}, amount0: ${amount0}, amount1: ${amount1}, sqrtPriceX96: ${sqrtPriceX96}`\n        // );\n        return true;\n        // JSBI.equal(amount0, param.amount0) &&\n        // JSBI.equal(amount1, param.amount1)\n        // &&\n        // JSBI.equal(sqrtPriceX96, param.sqrtPriceX96)\n      });\n    };\n\n    let solution1 = {\n      amountSpecified: JSBI.equal(param.liquidity, ZERO)\n        ? FullMath.incrTowardInfinity(param.amount0)\n        : param.amount0,\n      sqrtPriceLimitX96: undefined,\n    };\n    let solution2 = {\n      amountSpecified: JSBI.equal(param.liquidity, ZERO)\n        ? FullMath.incrTowardInfinity(param.amount1)\n        : param.amount1,\n      sqrtPriceLimitX96: undefined,\n    };\n    let solution3 = {\n      amountSpecified: param.amount0,\n      sqrtPriceLimitX96: undefined,\n    };\n    let solution4 = {\n      amountSpecified: param.amount1,\n      sqrtPriceLimitX96: undefined,\n    };\n    let solutionList: {\n      amountSpecified: JSBI;\n      sqrtPriceLimitX96?: JSBI;\n    }[] = [];\n\n    solutionList.push(solution1);\n    // solutionList.push(solution2);\n    // solutionList.push(solution3);\n    // solutionList.push(solution4);\n\n    // if (JSBI.notEqual(param.sqrtPriceX96, this.getCorePool().sqrtPriceX96)) {\n    //   if (JSBI.equal(param.liquidity, JSBI.BigInt(-1))) {\n    //     let solution5 = {\n    //       amountSpecified: param.amount0,\n    //       sqrtPriceLimitX96: param.sqrtPriceX96,\n    //     };\n    //     let solution6 = {\n    //       amountSpecified: param.amount1,\n    //       sqrtPriceLimitX96: param.sqrtPriceX96,\n    //     };\n    //     solutionList.push(solution5);\n    //     solutionList.push(solution6);\n    //   }\n    //   // solutionList.push(solution1);\n    //   // solutionList.push(solution2);\n    // }\n    for (let i = 0; i < solutionList.length; i++) {\n      if (\n        await tryWithDryRun(\n          i + 1,\n          param,\n          solutionList[i].amountSpecified,\n          solutionList[i].sqrtPriceLimitX96\n        )\n      ) {\n        return {\n          amountSpecified: solutionList[i].amountSpecified,\n          sqrtPriceX96: solutionList[i].sqrtPriceLimitX96,\n        };\n      }\n    }\n    throw new Error(\n      \"Can't resolve to the same as event records. Please check event input.\"\n    );\n  }\n\n  // user custom PostProcessor will be called after pool state transition finishes\n  updatePostProcessor(\n    callback: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ) {\n    this.postProcessorCallback = callback;\n  }\n\n  takeSnapshot(description: string): boolean {\n    if (this.poolState.hasSnapshot()) return false;\n    this.poolState.takeSnapshot(\n      description,\n      this.corePool.token0Balance,\n      this.corePool.token1Balance,\n      this.corePool.sqrtPriceX96,\n      this.corePool.liquidity,\n      this.corePool.tickCurrent,\n      this.corePool.feeGrowthGlobal0X128,\n      this.corePool.feeGrowthGlobal1X128,\n      this.corePool.tickManager,\n      this.corePool.positionManager\n    );\n    return true;\n  }\n\n  fork(): IConfigurableCorePool {\n    this.takeSnapshot(\"Automated for forking\");\n    return new ConfigurableCorePool(\n      this.poolState.fork(),\n      this.simulatorRoadmapManager,\n      this.simulatorConsoleVisitor,\n      this.simulatorPersistenceVisitor\n    );\n  }\n\n  persistSnapshot(): Promise<string> {\n    return this.traversePoolStateChain(\n      this.simulatorPersistenceVisitor,\n      this.poolState.id,\n      this.poolState.id\n    ).then(() => Promise.resolve(this.poolState.id));\n  }\n\n  stepBack() {\n    let fromTransition = this.poolState.transitionSource;\n    if (!fromTransition) {\n      throw new Error(\"This is already initial poolState.\");\n    }\n    this.recover(fromTransition.source.id);\n  }\n\n  recover(poolStateId: string) {\n    if (!this.simulatorRoadmapManager.hasPoolState(poolStateId)) {\n      throw new Error(\"Can't find poolState, id: \" + poolStateId);\n    }\n    let poolState: PoolState =\n      this.simulatorRoadmapManager.getPoolState(poolStateId)!;\n    this._poolState = poolState;\n    this.corePool = poolState.recoverCorePool();\n  }\n\n  get poolState(): PoolState {\n    return this._poolState;\n  }\n\n  accept(visitor: SimulatorVisitor): Promise<string> {\n    return visitor.visitConfigurableCorePool(this);\n  }\n\n  // this will take snapshot during PoolStates to speed up\n  showStateTransitionRoute(\n    toPoolStateId?: string,\n    fromPoolStateId?: string\n  ): Promise<void> {\n    let simulatorConsoleVisitor: SimulatorVisitor =\n      new SimulatorConsoleVisitor();\n    return this.traversePoolStateChain(\n      simulatorConsoleVisitor,\n      toPoolStateId ? toPoolStateId : this.poolState.id,\n      fromPoolStateId\n    );\n  }\n\n  persistSnapshots(\n    toPoolStateId: string,\n    fromPoolStateId?: string\n  ): Promise<Array<number>> {\n    let snapshotIds: Array<number> = [];\n    let poolStateVisitCallback = (_: PoolState, returnValue: number) => {\n      if (returnValue > 0) snapshotIds.push(returnValue);\n    };\n    return this.traversePoolStateChain(\n      this.simulatorPersistenceVisitor,\n      toPoolStateId,\n      fromPoolStateId,\n      poolStateVisitCallback\n    ).then(() => Promise.resolve(snapshotIds));\n  }\n\n  private traversePoolStateChain(\n    visitor: SimulatorVisitor,\n    toPoolStateId: string,\n    fromPoolStateId?: string,\n    poolStateVisitCallback?: (poolState: PoolState, returnValue: any) => void\n  ): Promise<void> {\n    if (\n      fromPoolStateId &&\n      !this.simulatorRoadmapManager.hasPoolState(fromPoolStateId)\n    ) {\n      throw new Error(\"Can't find poolState, id: \" + fromPoolStateId);\n    }\n    if (!this.simulatorRoadmapManager.hasPoolState(toPoolStateId)) {\n      throw new Error(\"Can't find poolState, id: \" + toPoolStateId);\n    }\n    let toPoolState: PoolState =\n      this.simulatorRoadmapManager.getPoolState(toPoolStateId)!;\n    return this.accept(visitor).then(() =>\n      this.handleSingleStepOnChain(\n        toPoolState,\n        visitor,\n        fromPoolStateId,\n        poolStateVisitCallback\n      )\n    );\n  }\n\n  private handleSingleStepOnChain(\n    poolState: PoolState,\n    simulatorVisitor: SimulatorVisitor,\n    fromPoolStateId?: string,\n    poolStateVisitCallback?: (poolState: PoolState, returnValue: any) => void\n  ): Promise<void> {\n    if (\n      !poolState.transitionSource ||\n      poolState.transitionSource.record.actionType == ActionType.FORK ||\n      poolState.id == fromPoolStateId\n    ) {\n      return poolState\n        .accept(simulatorVisitor, poolStateVisitCallback)\n        .then(() => Promise.resolve());\n    } else {\n      let fromTransition = poolState.transitionSource!;\n      return (\n        this.handleSingleStepOnChain(\n          fromTransition.source,\n          simulatorVisitor,\n          fromPoolStateId,\n          poolStateVisitCallback\n        )\n          .then(() => fromTransition.accept(simulatorVisitor))\n          .then(() =>\n            poolState.accept(simulatorVisitor, poolStateVisitCallback)\n          )\n          // this will clear auto caching of snapshot in last PoolState to save memory space\n          .then(() => fromTransition.source.clearSnapshot(true))\n          .then(() => Promise.resolve())\n      );\n    }\n  }\n\n  private buildRecord(\n    actionType: ActionType,\n    actionParams: MethodParams,\n    actionReturnValues: ReturnParams\n  ): Record {\n    return {\n      id: IdGenerator.guid(),\n      actionType,\n      actionParams,\n      actionReturnValues,\n      timestamp: new Date(),\n    };\n  }\n\n  private getNextPoolState(fromTransition: Transition) {\n    let nextPoolState = new PoolState(\n      this.poolState.poolConfig,\n      undefined,\n      fromTransition\n    );\n    fromTransition.target = nextPoolState;\n    return nextPoolState;\n  }\n\n  private postProcess(\n    actionType: ActionType,\n    actionParams: MethodParams,\n    actionReturnValues: ReturnParams,\n    postProcessorCallback?: (\n      configurableCorePool: IConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<void> {\n    let record: Record = this.buildRecord(\n      actionType,\n      actionParams,\n      actionReturnValues\n    );\n    let transition: Transition = this.poolState.addTransitionTarget(record);\n    let nextPoolState: PoolState = this.getNextPoolState(transition);\n    this.simulatorRoadmapManager.addPoolState(nextPoolState);\n    this._poolState = nextPoolState;\n    let postProcessor = postProcessorCallback\n      ? postProcessorCallback\n      : this.postProcessorCallback;\n    return postProcessor(this, transition);\n  }\n}\n"
  },
  {
    "path": "src/core/CorePool.ts",
    "content": "import JSBI from \"jsbi\";\nimport assert from \"assert\";\nimport { TickManager } from \"../manager/TickManager\";\nimport { PositionManager } from \"../manager/PositionManager\";\nimport { Position } from \"../model/Position\";\nimport { TickMath } from \"../util/TickMath\";\nimport { SqrtPriceMath } from \"../util/SqrtPriceMath\";\nimport { StepComputations } from \"../entity/StepComputations\";\nimport {\n  ZERO,\n  ONE,\n  NEGATIVE_ONE,\n  Q128,\n  PROTOCOL_FEE_DENOMINATOR,\n  PROTOCOL_FEE,\n} from \"../enum/InternalConstants\";\nimport { SwapMath } from \"../util/SwapMath\";\nimport { LiquidityMath } from \"../util/LiquidityMath\";\nimport { FullMath } from \"../util/FullMath\";\nimport { FeeAmount } from \"../enum/FeeAmount\";\nimport { TickView } from \"../interface/TickView\";\nimport { PositionView } from \"../interface/PositionView\";\n\nexport class CorePool {\n  readonly token0: string;\n  readonly token1: string;\n  readonly fee: FeeAmount;\n  readonly tickSpacing: number;\n  readonly maxLiquidityPerTick: JSBI;\n  private _token0Balance: JSBI;\n  private _token1Balance: JSBI;\n  private _sqrtPriceX96: JSBI;\n  private _liquidity: JSBI;\n  private _tickCurrent: number;\n  private _feeGrowthGlobal0X128: JSBI;\n  private _feeGrowthGlobal1X128: JSBI;\n  private _tickManager: TickManager;\n  private _positionManager: PositionManager;\n\n  constructor(\n    token0: string,\n    token1: string,\n    fee: FeeAmount,\n    tickSpacing: number,\n    token0Balance: JSBI = JSBI.BigInt(0),\n    token1Balance: JSBI = JSBI.BigInt(0),\n    sqrtPriceX96: JSBI = JSBI.BigInt(0),\n    liquidity: JSBI = JSBI.BigInt(0),\n    tickCurrent: number = 0,\n    feeGrowthGlobal0X128: JSBI = JSBI.BigInt(0),\n    feeGrowthGlobal1X128: JSBI = JSBI.BigInt(0),\n    tickManager: TickManager = new TickManager(),\n    positionManager: PositionManager = new PositionManager()\n  ) {\n    this.token0 = token0;\n    this.token1 = token1;\n    this.fee = fee;\n    this.tickSpacing = tickSpacing;\n    this.maxLiquidityPerTick =\n      TickMath.tickSpacingToMaxLiquidityPerTick(tickSpacing);\n    this._token0Balance = token0Balance;\n    this._token1Balance = token1Balance;\n    this._sqrtPriceX96 = sqrtPriceX96;\n    this._liquidity = liquidity;\n    this._tickCurrent = tickCurrent;\n    this._feeGrowthGlobal0X128 = feeGrowthGlobal0X128;\n    this._feeGrowthGlobal1X128 = feeGrowthGlobal1X128;\n    this._tickManager = tickManager;\n    this._positionManager = positionManager;\n  }\n\n  public get token0Balance(): JSBI {\n    return this._token0Balance;\n  }\n\n  public get token1Balance(): JSBI {\n    return this._token1Balance;\n  }\n\n  public get sqrtPriceX96(): JSBI {\n    return this._sqrtPriceX96;\n  }\n\n  public get liquidity(): JSBI {\n    return this._liquidity;\n  }\n\n  public get tickCurrent(): number {\n    return this._tickCurrent;\n  }\n\n  public get feeGrowthGlobal0X128(): JSBI {\n    return this._feeGrowthGlobal0X128;\n  }\n\n  public get feeGrowthGlobal1X128(): JSBI {\n    return this._feeGrowthGlobal1X128;\n  }\n\n  public get tickManager(): TickManager {\n    return this._tickManager;\n  }\n\n  public get positionManager(): PositionManager {\n    return this._positionManager;\n  }\n\n  initialize(sqrtPriceX96: JSBI) {\n    assert(JSBI.equal(this.sqrtPriceX96, ZERO), \"Already initialized!\");\n    this._tickCurrent = TickMath.getTickAtSqrtRatio(sqrtPriceX96);\n    this._sqrtPriceX96 = sqrtPriceX96;\n  }\n\n  /**\n   * Set the core global state of the pool\n   * This is used to set the core global state of the pool when we want to set the core global state of the pool without replaying the events. e.g. arbitrage.\n   * @param sqrtPriceX96 - The new sqrt price of the pool\n   * @param liquidity - The new liquidity of the pool\n   * @param tickCurrent - The new current tick of the pool\n   */\n  setCoreGlobalState(sqrtPriceX96: JSBI, liquidity: JSBI, tickCurrent: number) {\n    this._sqrtPriceX96 = sqrtPriceX96;\n    this._liquidity = liquidity;\n    this._tickCurrent = tickCurrent;\n  }\n\n  phantomMint(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    assert(JSBI.greaterThan(amount, ZERO), \"Mint amount should greater than 0\");\n\n    let amount0 = ZERO;\n    let amount1 = ZERO;\n\n    let positionStep = this.phantomModifyPosition(\n      recipient,\n      tickLower,\n      tickUpper,\n      amount\n    );\n\n    amount0 = positionStep.amount0;\n    amount1 = positionStep.amount1;\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  rapidMint(\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    assert(JSBI.greaterThan(amount, ZERO), \"Mint amount should greater than 0\");\n\n    let amount0 = ZERO;\n    let amount1 = ZERO;\n\n    let positionStep = this.rapidModifyPosition(tickLower, tickUpper, amount);\n\n    amount0 = positionStep.amount0;\n    amount1 = positionStep.amount1;\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  mint(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    assert(JSBI.greaterThan(amount, ZERO), \"Mint amount should greater than 0\");\n\n    let amount0 = ZERO;\n    let amount1 = ZERO;\n\n    let positionStep = this.modifyPosition(\n      recipient,\n      tickLower,\n      tickUpper,\n      amount\n    );\n\n    amount0 = positionStep.amount0;\n    amount1 = positionStep.amount1;\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  phantomBurn(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    let { position, amount0, amount1 } = this.phantomModifyPosition(\n      owner,\n      tickLower,\n      tickUpper,\n      JSBI.unaryMinus(amount)\n    );\n\n    amount0 = JSBI.unaryMinus(amount0);\n    amount1 = JSBI.unaryMinus(amount1);\n\n    if (JSBI.greaterThan(amount0, ZERO) || JSBI.greaterThan(amount1, ZERO)) {\n      let newTokensOwed0 = JSBI.add(position.tokensOwed0, amount0);\n      let newTokensOwed1 = JSBI.add(position.tokensOwed1, amount1);\n\n      position.updateBurn(newTokensOwed0, newTokensOwed1);\n    }\n\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  rapidBurn(\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    let { amount0, amount1 } = this.rapidModifyPosition(\n      tickLower,\n      tickUpper,\n      JSBI.unaryMinus(amount)\n    );\n\n    amount0 = JSBI.unaryMinus(amount0);\n    amount1 = JSBI.unaryMinus(amount1);\n\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  burn(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    let { position, amount0, amount1 } = this.modifyPosition(\n      owner,\n      tickLower,\n      tickUpper,\n      JSBI.unaryMinus(amount)\n    );\n\n    amount0 = JSBI.unaryMinus(amount0);\n    amount1 = JSBI.unaryMinus(amount1);\n\n    if (JSBI.greaterThan(amount0, ZERO) || JSBI.greaterThan(amount1, ZERO)) {\n      let newTokensOwed0 = JSBI.add(position.tokensOwed0, amount0);\n      let newTokensOwed1 = JSBI.add(position.tokensOwed1, amount1);\n\n      position.updateBurn(newTokensOwed0, newTokensOwed1);\n    }\n\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  collect(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount0Requested: JSBI,\n    amount1Requested: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    this.checkTicks(tickLower, tickUpper);\n\n    let { amount0, amount1 } = this.positionManager.collectPosition(\n      recipient,\n      tickLower,\n      tickUpper,\n      amount0Requested,\n      amount1Requested\n    );\n\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  querySwap(\n    zeroForOne: boolean,\n    amountSpecified: JSBI,\n    sqrtPriceLimitX96?: JSBI\n  ): { amount0: JSBI; amount1: JSBI; sqrtPriceX96: JSBI } {\n    return this.handleSwap(\n      zeroForOne,\n      amountSpecified,\n      sqrtPriceLimitX96,\n      true\n    );\n  }\n\n  swap(\n    zeroForOne: boolean,\n    amountSpecified: JSBI,\n    sqrtPriceLimitX96?: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    return this.handleSwap(\n      zeroForOne,\n      amountSpecified,\n      sqrtPriceLimitX96,\n      false\n    );\n  }\n\n  private handleSwap(\n    zeroForOne: boolean,\n    amountSpecified: JSBI,\n    sqrtPriceLimitX96: JSBI | undefined,\n    isStatic: boolean\n  ): { amount0: JSBI; amount1: JSBI; sqrtPriceX96: JSBI } {\n    if (!sqrtPriceLimitX96)\n      sqrtPriceLimitX96 = zeroForOne\n        ? JSBI.add(TickMath.MIN_SQRT_RATIO, ONE)\n        : JSBI.subtract(TickMath.MAX_SQRT_RATIO, ONE);\n\n    if (zeroForOne) {\n      assert(\n        JSBI.greaterThan(sqrtPriceLimitX96, TickMath.MIN_SQRT_RATIO),\n        \"RATIO_MIN\"\n      );\n      assert(\n        JSBI.lessThan(sqrtPriceLimitX96, this.sqrtPriceX96),\n        \"RATIO_CURRENT\"\n      );\n    } else {\n      assert(\n        JSBI.lessThan(sqrtPriceLimitX96, TickMath.MAX_SQRT_RATIO),\n        \"RATIO_MAX\"\n      );\n      assert(\n        JSBI.greaterThan(sqrtPriceLimitX96, this.sqrtPriceX96),\n        \"RATIO_CURRENT\"\n      );\n    }\n\n    const exactInput = JSBI.greaterThanOrEqual(amountSpecified, ZERO);\n\n    // keep track of swap state\n\n    const state = {\n      amountSpecifiedRemaining: amountSpecified,\n      amountCalculated: ZERO,\n      sqrtPriceX96: this.sqrtPriceX96,\n      tick: this.tickCurrent,\n      liquidity: this.liquidity,\n      feeGrowthGlobalX128: zeroForOne\n        ? this._feeGrowthGlobal0X128\n        : this._feeGrowthGlobal1X128,\n    };\n\n    // start swap while loop\n    while (\n      JSBI.notEqual(state.amountSpecifiedRemaining, ZERO) &&\n      JSBI.notEqual(state.sqrtPriceX96, sqrtPriceLimitX96)\n    ) {\n      let step: StepComputations = {\n        sqrtPriceStartX96: ZERO,\n        tickNext: 0,\n        initialized: false,\n        sqrtPriceNextX96: ZERO,\n        amountIn: ZERO,\n        amountOut: ZERO,\n        feeAmount: ZERO,\n      };\n      step.sqrtPriceStartX96 = state.sqrtPriceX96;\n\n      // because each iteration of the while loop rounds, we can't optimize this code (relative to the smart contract)\n      // by simply traversing to the next available tick, we instead need to exactly replicate\n      // tickBitmap.nextInitializedTickWithinOneWord\n      ({ nextTick: step.tickNext, initialized: step.initialized } =\n        this.tickManager.getNextInitializedTick(\n          state.tick,\n          this.tickSpacing,\n          zeroForOne\n        ));\n\n      if (step.tickNext < TickMath.MIN_TICK) {\n        step.tickNext = TickMath.MIN_TICK;\n      } else if (step.tickNext > TickMath.MAX_TICK) {\n        step.tickNext = TickMath.MAX_TICK;\n      }\n\n      step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);\n      [state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount] =\n        SwapMath.computeSwapStep(\n          state.sqrtPriceX96,\n          (\n            zeroForOne\n              ? JSBI.lessThan(step.sqrtPriceNextX96, sqrtPriceLimitX96)\n              : JSBI.greaterThan(step.sqrtPriceNextX96, sqrtPriceLimitX96)\n          )\n            ? sqrtPriceLimitX96\n            : step.sqrtPriceNextX96,\n          state.liquidity,\n          state.amountSpecifiedRemaining,\n          this.fee\n        );\n\n      if (exactInput) {\n        state.amountSpecifiedRemaining = JSBI.subtract(\n          state.amountSpecifiedRemaining,\n          JSBI.add(step.amountIn, step.feeAmount)\n        );\n        state.amountCalculated = JSBI.subtract(\n          state.amountCalculated,\n          step.amountOut\n        );\n      } else {\n        state.amountSpecifiedRemaining = JSBI.add(\n          state.amountSpecifiedRemaining,\n          step.amountOut\n        );\n        state.amountCalculated = JSBI.add(\n          state.amountCalculated,\n          JSBI.add(step.amountIn, step.feeAmount)\n        );\n      }\n\n      //   // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee\n      //   if (cache.feeProtocol > 0) {\n      //     uint256 delta = (step.feeAmount.mul(cache.feeProtocol)) / PROTOCOL_FEE_DENOMINATOR;\n      //     step.feeAmount -= delta;\n      //     state.protocolFee += uint128(delta);\n      // }\n\n      // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee\n      let delta = JSBI.divide(\n        JSBI.multiply(step.feeAmount, PROTOCOL_FEE),\n        PROTOCOL_FEE_DENOMINATOR\n      );\n      step.feeAmount = JSBI.subtract(step.feeAmount, delta);\n\n      if (JSBI.greaterThan(state.liquidity, ZERO))\n        state.feeGrowthGlobalX128 = JSBI.add(\n          state.feeGrowthGlobalX128,\n          FullMath.mulDiv(step.feeAmount, Q128, state.liquidity)\n        );\n\n      if (JSBI.equal(state.sqrtPriceX96, step.sqrtPriceNextX96)) {\n        // if the tick is initialized, run the tick transition\n        if (step.initialized) {\n          let nextTick = this.tickManager.getTickAndInitIfAbsent(step.tickNext);\n          let liquidityNet = isStatic\n            ? nextTick.liquidityNet\n            : nextTick.cross(\n                zeroForOne\n                  ? state.feeGrowthGlobalX128\n                  : this._feeGrowthGlobal0X128,\n                zeroForOne\n                  ? this._feeGrowthGlobal1X128\n                  : state.feeGrowthGlobalX128\n              );\n\n          // if we're moving leftward, we interpret liquidityNet as the opposite sign\n          // safe because liquidityNet cannot be type(int128).min\n          if (zeroForOne)\n            liquidityNet = JSBI.multiply(liquidityNet, NEGATIVE_ONE);\n\n          state.liquidity = LiquidityMath.addDelta(\n            state.liquidity,\n            liquidityNet\n          );\n        }\n\n        state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;\n      } else if (JSBI.notEqual(state.sqrtPriceX96, step.sqrtPriceStartX96)) {\n        // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved\n        state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);\n      }\n    }\n\n    if (!isStatic) {\n      this._sqrtPriceX96 = state.sqrtPriceX96;\n      if (state.tick != this.tickCurrent) this._tickCurrent = state.tick;\n      if (JSBI.notEqual(state.liquidity, this._liquidity))\n        this._liquidity = state.liquidity;\n\n      // update fee growth global\n      if (zeroForOne) {\n        this._feeGrowthGlobal0X128 = state.feeGrowthGlobalX128;\n      } else {\n        this._feeGrowthGlobal1X128 = state.feeGrowthGlobalX128;\n      }\n    }\n\n    let [amount0, amount1] =\n      zeroForOne == exactInput\n        ? [\n            JSBI.subtract(amountSpecified, state.amountSpecifiedRemaining),\n            state.amountCalculated,\n          ]\n        : [\n            state.amountCalculated,\n            JSBI.subtract(amountSpecified, state.amountSpecifiedRemaining),\n          ];\n\n    return { amount0, amount1, sqrtPriceX96: state.sqrtPriceX96 };\n  }\n\n  private checkTicks(tickLower: number, tickUpper: number) {\n    assert(tickLower < tickUpper, \"tickLower should lower than tickUpper\");\n    assert(\n      tickLower >= TickMath.MIN_TICK,\n      \"tickLower should NOT lower than MIN_TICK\"\n    );\n    assert(\n      tickUpper <= TickMath.MAX_TICK,\n      \"tickUpper should NOT greater than MAX_TICK\"\n    );\n  }\n\n  private phantomModifyPosition(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    liquidityDelta: JSBI\n  ): { position: Position; amount0: JSBI; amount1: JSBI } {\n    this.checkTicks(tickLower, tickUpper);\n\n    let amount0: JSBI = ZERO,\n      amount1: JSBI = ZERO;\n\n    let positionView: PositionView = this.getPosition(\n      owner,\n      tickLower,\n      tickUpper\n    );\n\n    if (JSBI.lessThan(liquidityDelta, ZERO)) {\n      const negatedLiquidityDelta = JSBI.multiply(liquidityDelta, NEGATIVE_ONE);\n      assert(\n        JSBI.greaterThanOrEqual(positionView.liquidity, negatedLiquidityDelta),\n        \"Liquidity Underflow\"\n      );\n    }\n\n    // check ticks pass, update position\n    let position = this.phantomUpdatePosition(\n      owner,\n      tickLower,\n      tickUpper,\n      liquidityDelta\n    );\n    // use switch or pattern matching\n    // check if liquidity happen add() or remove()\n    if (JSBI.notEqual(liquidityDelta, ZERO)) {\n      if (this.tickCurrent < tickLower) {\n        amount0 = SqrtPriceMath.getAmount0Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n      } else if (this.tickCurrent < tickUpper) {\n        amount0 = SqrtPriceMath.getAmount0Delta(\n          this._sqrtPriceX96,\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n\n        amount1 = SqrtPriceMath.getAmount1Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          this._sqrtPriceX96,\n          liquidityDelta\n        );\n      } else {\n        amount1 = SqrtPriceMath.getAmount1Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n      }\n    }\n\n    return {\n      position,\n      amount0,\n      amount1,\n    };\n  }\n\n  private rapidModifyPosition(\n    tickLower: number,\n    tickUpper: number,\n    liquidityDelta: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    this.checkTicks(tickLower, tickUpper);\n\n    let amount0: JSBI = ZERO,\n      amount1: JSBI = ZERO;\n\n    // check ticks pass, update position\n    this.rapidUpdatePosition(tickLower, tickUpper, liquidityDelta);\n\n    // use switch or pattern matching\n    // check if liquidity happen add() or remove()\n    if (JSBI.notEqual(liquidityDelta, ZERO)) {\n      if (this.tickCurrent < tickLower) {\n        amount0 = SqrtPriceMath.getAmount0Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n      } else if (this.tickCurrent < tickUpper) {\n        amount0 = SqrtPriceMath.getAmount0Delta(\n          this._sqrtPriceX96,\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n\n        amount1 = SqrtPriceMath.getAmount1Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          this._sqrtPriceX96,\n          liquidityDelta\n        );\n\n        this._liquidity = LiquidityMath.addDelta(\n          this._liquidity,\n          liquidityDelta\n        );\n      } else {\n        amount1 = SqrtPriceMath.getAmount1Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n      }\n    }\n\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  private modifyPosition(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    liquidityDelta: JSBI\n  ): { position: Position; amount0: JSBI; amount1: JSBI } {\n    this.checkTicks(tickLower, tickUpper);\n\n    let amount0: JSBI = ZERO,\n      amount1: JSBI = ZERO;\n\n    let positionView: PositionView = this.getPosition(\n      owner,\n      tickLower,\n      tickUpper\n    );\n\n    if (JSBI.lessThan(liquidityDelta, ZERO)) {\n      const negatedLiquidityDelta = JSBI.multiply(liquidityDelta, NEGATIVE_ONE);\n      assert(\n        JSBI.greaterThanOrEqual(positionView.liquidity, negatedLiquidityDelta),\n        \"Liquidity Underflow\"\n      );\n    }\n\n    // check ticks pass, update position\n    let position = this.updatePosition(\n      owner,\n      tickLower,\n      tickUpper,\n      liquidityDelta\n    );\n    // use switch or pattern matching\n    // check if liquidity happen add() or remove()\n    if (JSBI.notEqual(liquidityDelta, ZERO)) {\n      if (this.tickCurrent < tickLower) {\n        amount0 = SqrtPriceMath.getAmount0Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n      } else if (this.tickCurrent < tickUpper) {\n        amount0 = SqrtPriceMath.getAmount0Delta(\n          this._sqrtPriceX96,\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n\n        amount1 = SqrtPriceMath.getAmount1Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          this._sqrtPriceX96,\n          liquidityDelta\n        );\n\n        this._liquidity = LiquidityMath.addDelta(\n          this._liquidity,\n          liquidityDelta\n        );\n      } else {\n        amount1 = SqrtPriceMath.getAmount1Delta(\n          TickMath.getSqrtRatioAtTick(tickLower),\n          TickMath.getSqrtRatioAtTick(tickUpper),\n          liquidityDelta\n        );\n      }\n    }\n\n    return {\n      position,\n      amount0,\n      amount1,\n    };\n  }\n\n  private phantomUpdatePosition(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    liquidityDelta: JSBI\n  ): Position {\n    let position: Position = this.positionManager.getPositionAndInitIfAbsent(\n      owner,\n      tickLower,\n      tickUpper\n    );\n\n    let feeGrowthInsideStep = this.tickManager.getFeeGrowthInside(\n      tickLower,\n      tickUpper,\n      this.tickCurrent,\n      this.feeGrowthGlobal0X128,\n      this.feeGrowthGlobal1X128\n    );\n\n    position.update(\n      liquidityDelta,\n      feeGrowthInsideStep.feeGrowthInside0X128,\n      feeGrowthInsideStep.feeGrowthInside1X128\n    );\n\n    return position;\n  }\n\n  private rapidUpdatePosition(\n    tickLower: number,\n    tickUpper: number,\n    liquidityDelta: JSBI\n  ) {\n    let flippedLower: boolean = false;\n    let flippedUpper: boolean = false;\n\n    if (JSBI.notEqual(liquidityDelta, ZERO)) {\n      flippedLower = this.tickManager\n        .getTickAndInitIfAbsent(tickLower)\n        .update(\n          liquidityDelta,\n          this.tickCurrent,\n          this.feeGrowthGlobal0X128,\n          this.feeGrowthGlobal1X128,\n          false,\n          this.maxLiquidityPerTick\n        );\n\n      flippedUpper = this.tickManager\n        .getTickAndInitIfAbsent(tickUpper)\n        .update(\n          liquidityDelta,\n          this.tickCurrent,\n          this.feeGrowthGlobal0X128,\n          this.feeGrowthGlobal1X128,\n          true,\n          this.maxLiquidityPerTick\n        );\n    }\n\n    if (JSBI.lessThan(liquidityDelta, ZERO)) {\n      if (flippedLower) {\n        this.tickManager.clear(tickLower);\n      }\n      if (flippedUpper) {\n        this.tickManager.clear(tickUpper);\n      }\n    }\n  }\n\n  private updatePosition(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    liquidityDelta: JSBI\n  ): Position {\n    let position: Position = this.positionManager.getPositionAndInitIfAbsent(\n      owner,\n      tickLower,\n      tickUpper\n    );\n\n    let flippedLower: boolean = false;\n    let flippedUpper: boolean = false;\n\n    if (JSBI.notEqual(liquidityDelta, ZERO)) {\n      flippedLower = this.tickManager\n        .getTickAndInitIfAbsent(tickLower)\n        .update(\n          liquidityDelta,\n          this.tickCurrent,\n          this.feeGrowthGlobal0X128,\n          this.feeGrowthGlobal1X128,\n          false,\n          this.maxLiquidityPerTick\n        );\n\n      flippedUpper = this.tickManager\n        .getTickAndInitIfAbsent(tickUpper)\n        .update(\n          liquidityDelta,\n          this.tickCurrent,\n          this.feeGrowthGlobal0X128,\n          this.feeGrowthGlobal1X128,\n          true,\n          this.maxLiquidityPerTick\n        );\n    }\n\n    let feeGrowthInsideStep = this.tickManager.getFeeGrowthInside(\n      tickLower,\n      tickUpper,\n      this.tickCurrent,\n      this.feeGrowthGlobal0X128,\n      this.feeGrowthGlobal1X128\n    );\n\n    position.update(\n      liquidityDelta,\n      feeGrowthInsideStep.feeGrowthInside0X128,\n      feeGrowthInsideStep.feeGrowthInside1X128\n    );\n\n    if (JSBI.lessThan(liquidityDelta, ZERO)) {\n      if (flippedLower) {\n        this.tickManager.clear(tickLower);\n      }\n      if (flippedUpper) {\n        this.tickManager.clear(tickUpper);\n      }\n    }\n\n    return position;\n  }\n\n  getTickMap(): Map<number, TickView> {\n    return this.tickManager.sortedTicks;\n  }\n\n  getTick(tick: number): TickView {\n    return this.tickManager.getTickReadonly(tick);\n  }\n\n  getPosition(\n    owner: string,\n    tickLower: number,\n    tickUpper: number\n  ): PositionView {\n    return this.positionManager.getPositionReadonly(\n      owner,\n      tickLower,\n      tickUpper\n    );\n  }\n\n  getPositionBalance(\n    owner: string,\n    tickLower: number,\n    tickUpper: number\n  ): {\n    amount0: JSBI;\n    amount1: JSBI;\n  } {\n    let position = this.getPosition(owner, tickLower, tickUpper);\n    let amount0: JSBI = ZERO;\n    let amount1: JSBI = ZERO;\n\n    if (this.tickCurrent < tickLower) {\n      amount0 = SqrtPriceMath.getAmount0Delta(\n        TickMath.getSqrtRatioAtTick(tickLower),\n        TickMath.getSqrtRatioAtTick(tickUpper),\n        position.liquidity\n      );\n    } else if (this.tickCurrent < tickUpper) {\n      amount0 = SqrtPriceMath.getAmount0Delta(\n        this._sqrtPriceX96,\n        TickMath.getSqrtRatioAtTick(tickUpper),\n        position.liquidity\n      );\n\n      amount1 = SqrtPriceMath.getAmount1Delta(\n        TickMath.getSqrtRatioAtTick(tickLower),\n        this._sqrtPriceX96,\n        position.liquidity\n      );\n    } else {\n      amount1 = SqrtPriceMath.getAmount1Delta(\n        TickMath.getSqrtRatioAtTick(tickLower),\n        TickMath.getSqrtRatioAtTick(tickUpper),\n        position.liquidity\n      );\n    }\n    return {\n      amount0,\n      amount1,\n    };\n  }\n\n  toString(): string {\n    return `\n    Current State:\n        token0Balance: ${this.token0Balance.toString()}\n        token1Balance: ${this.token1Balance.toString()}\n        sqrtPriceX96: ${this.sqrtPriceX96.toString()}\n        liquidity: ${this.liquidity.toString()}\n        tickCurrent: ${this.tickCurrent}\n        feeGrowthGlobal0X128: ${this.feeGrowthGlobal0X128.toString()}\n        feeGrowthGlobal1X128: ${this.feeGrowthGlobal1X128.toString()}\n    `;\n  }\n}\n"
  },
  {
    "path": "src/core/index.ts",
    "content": "export * from \"./CorePool\";\n"
  },
  {
    "path": "src/entity/EndBlockType.ts",
    "content": "export type EndBlockTypeWhenInit =\n  | number\n  | \"latest\"\n  | \"afterDeployment\"\n  | \"afterInitialization\";\n\nexport type EndBlockTypeWhenRecover =\n  | number\n  | \"latestOnChain\"\n  | \"latestDownloaded\"\n  | \"afterDeployment\"\n  | \"afterInitialization\";\n"
  },
  {
    "path": "src/entity/LiquidityEvent.ts",
    "content": "import { EventType } from \"../enum/EventType\";\nimport JSBI from \"jsbi\";\n\nexport interface LiquidityEvent {\n  id: number;\n  type: EventType.MINT | EventType.BURN;\n  msgSender: string;\n  recipient: string;\n  liquidity: JSBI;\n  amount0: JSBI;\n  amount1: JSBI;\n  tickLower: number;\n  tickUpper: number;\n  blockNumber: number;\n  transactionHash: string;\n  logIndex: number;\n  date: Date;\n  verified: boolean;\n}\n"
  },
  {
    "path": "src/entity/Record.ts",
    "content": "import { ActionType } from \"../enum/ActionType\";\nimport {\n  BurnParams,\n  CollectParams,\n  ForkParams,\n  GeneralReturnParams,\n  InitializeParams,\n  MintParams,\n  SwapParams,\n  VoidReturnParams,\n} from \"../interface/ActionParams\";\n\nexport type Record = {\n  id: string;\n  actionType: ActionType;\n  actionParams:\n    | InitializeParams\n    | MintParams\n    | BurnParams\n    | SwapParams\n    | CollectParams\n    | ForkParams;\n  actionReturnValues: GeneralReturnParams | VoidReturnParams;\n  timestamp: Date;\n};\n"
  },
  {
    "path": "src/entity/Snapshot.ts",
    "content": "import JSBI from \"jsbi\";\nimport { PositionManager } from \"../manager/PositionManager\";\nimport { TickManager } from \"../manager/TickManager\";\nimport { PoolConfig } from \"../model/PoolConfig\";\n\nexport type Snapshot = {\n  id: string;\n  description: string;\n  poolConfig: PoolConfig;\n  token0Balance: JSBI;\n  token1Balance: JSBI;\n  sqrtPriceX96: JSBI;\n  liquidity: JSBI;\n  tickCurrent: number;\n  feeGrowthGlobal0X128: JSBI;\n  feeGrowthGlobal1X128: JSBI;\n  tickManager: TickManager;\n  positionManager: PositionManager;\n  timestamp: Date;\n};\n"
  },
  {
    "path": "src/entity/SnapshotProfile.ts",
    "content": "export type SnapshotProfile = {\n  id: string;\n  description: string;\n};\n"
  },
  {
    "path": "src/entity/StepComputations.ts",
    "content": "import JSBI from \"jsbi\";\n\nexport type StepComputations = {\n  sqrtPriceStartX96: JSBI;\n  tickNext: number;\n  initialized: boolean;\n  sqrtPriceNextX96: JSBI;\n  amountIn: JSBI;\n  amountOut: JSBI;\n  feeAmount: JSBI;\n};\n"
  },
  {
    "path": "src/entity/SwapEvent.ts",
    "content": "import { EventType } from \"../enum/EventType\";\nimport JSBI from \"jsbi\";\n\nexport interface SwapEvent {\n  id: number;\n  type: EventType.SWAP;\n  msgSender: string;\n  recipient: string;\n  amount0: JSBI;\n  amount1: JSBI;\n  amountSpecified: JSBI;\n  sqrtPriceX96: JSBI;\n  liquidity: JSBI;\n  tick: number;\n  blockNumber: number;\n  transactionHash: string;\n  logIndex: number;\n  date: Date;\n  verified: boolean;\n}\n"
  },
  {
    "path": "src/entity/index.ts",
    "content": "export * from \"./Record\";\nexport * from \"./Snapshot\";\nexport * from \"./SnapshotProfile\";\nexport * from \"./EndBlockType\";\n"
  },
  {
    "path": "src/enum/ActionType.ts",
    "content": "export enum ActionType {\n  INITIALIZE = \"initialize\",\n  MINT = \"mint\",\n  BURN = \"burn\",\n  COLLECT = \"collect\",\n  SWAP = \"swap\",\n  FORK = \"fork\",\n}\n"
  },
  {
    "path": "src/enum/EventDataSourceType.ts",
    "content": "export enum EventDataSourceType {\n  SUBGRAPH = 1,\n  RPC = 2,\n}\n"
  },
  {
    "path": "src/enum/EventType.ts",
    "content": "export enum EventType {\n  MINT = 1,\n  BURN = 2,\n  SWAP = 3,\n}\n"
  },
  {
    "path": "src/enum/FeeAmount.ts",
    "content": "/**\n * The default factory enabled fee amounts, denominated in hundredths of bips.\n */\nexport enum FeeAmount {\n  EXTRA_LOW = 100,\n  LOW = 500,\n  MEDIUM = 3000,\n  HIGH = 10000,\n}\n"
  },
  {
    "path": "src/enum/InternalConstants.ts",
    "content": "import JSBI from \"jsbi\";\nimport { FeeAmount } from \"./FeeAmount\";\n\nimport dotenv from \"dotenv\";\n\n// load .env file\ndotenv.config();\n\nexport const SUBGRAPH_API_KEY = process.env.SUBGRAPH_API_KEY;\n\n// constants used internally but not expected to be used externally\nexport const UNISWAP_V3_SUBGRAPH_ENDPOINT =\n  \"https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3\";\n\nexport const BSC_PANCAKE_V3_SUBGRAPH_ENDPOINT = `https://gateway.thegraph.com/api/subgraphs/id/A1BC1hzDsK4NTeXBpKQnDBphngpYZAwDUF7dEBfa3jHK`;\n\nexport const NEGATIVE_ONE = JSBI.BigInt(-1);\nexport const ZERO = JSBI.BigInt(0);\nexport const ONE = JSBI.BigInt(1);\nexport const TWO = JSBI.BigInt(2);\nexport const MaxUint128 = JSBI.subtract(\n  JSBI.exponentiate(TWO, JSBI.BigInt(128)),\n  ONE\n);\nexport const MaxUint160 = JSBI.subtract(\n  JSBI.exponentiate(TWO, JSBI.BigInt(160)),\n  ONE\n);\nexport const MaxUint256 = JSBI.subtract(\n  JSBI.exponentiate(TWO, JSBI.BigInt(256)),\n  ONE\n);\nexport const MaxInt128 = JSBI.subtract(\n  JSBI.exponentiate(TWO, JSBI.BigInt(128 - 1)),\n  ONE\n);\nexport const MinInt128 = JSBI.unaryMinus(\n  JSBI.exponentiate(TWO, JSBI.BigInt(128 - 1))\n);\n\n// used in liquidity amount math\nexport const Q32 = JSBI.exponentiate(TWO, JSBI.BigInt(32));\nexport const Q96 = JSBI.exponentiate(TWO, JSBI.BigInt(96));\nexport const Q128 = JSBI.exponentiate(TWO, JSBI.BigInt(128));\nexport const Q192 = JSBI.exponentiate(Q96, TWO);\n\n// used in fee calculation\nexport const MAX_FEE = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(6));\n\nexport const PROTOCOL_FEE = JSBI.BigInt(3300);\nexport const PROTOCOL_FEE_DENOMINATOR = JSBI.BigInt(10000);\n\n// The default factory tick spacings by fee amount.\nexport const TICK_SPACINGS: { [amount in FeeAmount]: number } = {\n  [FeeAmount.EXTRA_LOW]: 1,\n  [FeeAmount.LOW]: 10,\n  [FeeAmount.MEDIUM]: 60,\n  [FeeAmount.HIGH]: 200,\n};\n\nexport const RAPID_POSITION_OWNER =\n  \"0x00000000000000000000000000000001ECAE3608\";\n"
  },
  {
    "path": "src/enum/index.ts",
    "content": "export * from \"./ActionType\";\nexport * from \"./FeeAmount\";\nexport * from \"./EventType\";\nexport * from \"./EventDataSourceType\";\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from \"./client\";\nexport * from \"./core\";\nexport * from \"./manager\";\nexport * from \"./entity\";\nexport * from \"./enum\";\nexport * from \"./interface\";\nexport * from \"./model\";\nexport * from \"./util\";\n"
  },
  {
    "path": "src/interface/ActionParams.ts",
    "content": "import JSBI from \"jsbi\";\nimport { ActionType } from \"../enum/ActionType\";\n\nexport type MethodParams =\n  | InitializeParams\n  | MintParams\n  | BurnParams\n  | SwapParams\n  | CollectParams\n  | ForkParams;\n\nexport interface InitializeParams {\n  type: ActionType.INITIALIZE;\n  sqrtPriceX96: JSBI;\n}\n\nexport interface MintParams {\n  type: ActionType.MINT;\n  recipient: string;\n  tickLower: number;\n  tickUpper: number;\n  amount: JSBI;\n}\n\nexport interface BurnParams {\n  type: ActionType.BURN;\n  owner: string;\n  tickLower: number;\n  tickUpper: number;\n  amount: JSBI;\n}\n\nexport interface SwapParams {\n  type: ActionType.SWAP;\n  zeroForOne: boolean;\n  amountSpecified: JSBI;\n  sqrtPriceLimitX96?: JSBI;\n}\n\nexport interface CollectParams {\n  type: ActionType.COLLECT;\n  recipient: string;\n  tickLower: number;\n  tickUpper: number;\n  amount0Requested: JSBI;\n  amount1Requested: JSBI;\n}\n\nexport interface ForkParams {\n  type: ActionType.FORK;\n}\n\nexport type ReturnParams = VoidReturnParams | GeneralReturnParams;\n\nexport interface VoidReturnParams {}\n\nexport interface GeneralReturnParams {\n  amount0: JSBI;\n  amount1: JSBI;\n}\n"
  },
  {
    "path": "src/interface/ConfigurableCorePool.ts",
    "content": "import JSBI from \"jsbi\";\nimport { SwapEvent } from \"../entity/SwapEvent\";\nimport { CorePoolView } from \"./CorePoolView\";\nimport { PoolStateView } from \"./PoolStateView\";\nimport { Transition as TransitionView } from \"./Transition\";\n\nexport interface ConfigurableCorePool {\n  readonly id: string;\n\n  getPoolState(): PoolStateView;\n\n  getCorePool(): CorePoolView;\n\n  initialize(sqrtPriceX96: JSBI): Promise<void>;\n\n  setCoreGlobalState(\n    sqrtPriceX96: JSBI,\n    liquidity: JSBI,\n    tickCurrent: number\n  ): void;\n\n  rapidMint(\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  phantomMint(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  mint(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  rapidBurn(\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  phantomBurn(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  burn(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    amount: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  collect(\n    recipient: string,\n    tickLower: number,\n    tickUpper: number,\n    amount0Requested: JSBI,\n    amount1Requested: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  swap(\n    zeroForOne: boolean,\n    amountSpecified: JSBI,\n    sqrtPriceLimitX96?: JSBI,\n    postProcessorCallback?: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): Promise<{ amount0: JSBI; amount1: JSBI }>;\n\n  querySwap(\n    zeroForOne: boolean,\n    amountSpecified: JSBI,\n    sqrtPriceLimitX96?: JSBI\n  ): Promise<{ amount0: JSBI; amount1: JSBI; sqrtPriceX96: JSBI }>;\n\n  resolveInputFromSwapResultEvent(\n    swapEvent: SwapEvent\n  ): Promise<{ amountSpecified: JSBI; sqrtPriceX96?: JSBI }>;\n\n  // user custom PostProcessor will be called after pool state transition finishes\n  updatePostProcessor(\n    callback: (\n      configurableCorePool: ConfigurableCorePool,\n      transition: TransitionView\n    ) => Promise<void>\n  ): void;\n\n  takeSnapshot(description: string): boolean;\n\n  fork(): ConfigurableCorePool;\n\n  persistSnapshot(): Promise<string>;\n\n  stepBack(): void;\n\n  recover(poolStateId: string): void;\n}\n"
  },
  {
    "path": "src/interface/CorePoolView.ts",
    "content": "import { CorePool } from \"../core/CorePool\";\n\nexport type CorePoolView = Pick<\n  CorePool,\n  {\n    [K in keyof CorePool]-?: CorePool[K] extends Function\n      ? K extends \"getTick\" | \"getPosition\" | \"querySwap\" | \"getPositionBalance\"\n        ? K\n        : never\n      : K;\n  }[keyof CorePool]\n>;\n"
  },
  {
    "path": "src/interface/PoolStateContainer.ts",
    "content": "import { PoolState } from \"../model/PoolState\";\n\nexport interface PoolStateContainer {\n  addPoolState: (PoolState: PoolState) => string;\n  getPoolState: (PoolStateId: string) => PoolState | undefined;\n  hasPoolState: (PoolStateId: string) => boolean;\n}\n"
  },
  {
    "path": "src/interface/PoolStateView.ts",
    "content": "import { PoolState } from \"../model/PoolState\";\n\nexport type PoolStateView = Pick<\n  PoolState,\n  {\n    [K in keyof PoolState]: PoolState[K] extends Function\n      ? K extends \"getTransitionSource\" | \"getTransitionTargets\"\n        ? K\n        : never\n      : K extends \"transitionSource\" | \"transitionTargets\"\n      ? never\n      : K;\n  }[keyof PoolState]\n>;\n"
  },
  {
    "path": "src/interface/PositionView.ts",
    "content": "import { Position } from \"../model/Position\";\n\nexport type PositionView = Pick<\n  Position,\n  {\n    [K in keyof Position]: Position[K] extends Function ? never : K;\n  }[keyof Position]\n>;\n"
  },
  {
    "path": "src/interface/SimulationDataManager.ts",
    "content": "import { PoolState } from \"../model/PoolState\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { Roadmap } from \"../model/Roadmap\";\nimport { SnapshotProfile } from \"../entity/SnapshotProfile\";\nimport { PoolConfig } from \"../model/PoolConfig\";\n\nexport interface SimulationDataManager {\n  persistRoadmap(roadmap: Roadmap): Promise<number>;\n\n  persistSnapshot(poolState: PoolState): Promise<number>;\n\n  getSnapshotProfiles(): Promise<SnapshotProfile[]>;\n\n  getSnapshots(snapshotIds: number[]): Promise<Snapshot[]>;\n\n  getSnapshot(snapshotId: string): Promise<Snapshot | undefined>;\n\n  getPoolConfig(poolConfigId: string): Promise<PoolConfig | undefined>;\n\n  getRoadmap(roadmapId: string): Promise<Roadmap | undefined>;\n\n  close(): Promise<void>;\n}\n"
  },
  {
    "path": "src/interface/SimulatorRoadmapManager.ts",
    "content": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\n\nexport interface SimulatorRoadmapManager {\n  printRoute(configurableCorePoolId: string): Promise<void>;\n\n  listRoutes(): Array<ConfigurableCorePool>;\n\n  persistRoute(\n    configurableCorePoolId: string,\n    description: string\n  ): Promise<string>;\n\n  loadAndPrintRoute(roadmapId: string): Promise<void>;\n}\n"
  },
  {
    "path": "src/interface/SimulatorVisitor.ts",
    "content": "import { PoolState } from \"../model/PoolState\";\nimport { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { Transition } from \"../model/Transition\";\n\nexport interface SimulatorVisitor {\n  visitTransition(\n    transition: Transition,\n    callback?: (transition: Transition, returnValue: any) => void\n  ): Promise<string>;\n  visitPoolState(\n    poolState: PoolState,\n    callback?: (poolState: PoolState, returnValue: any) => void\n  ): Promise<string>;\n  visitConfigurableCorePool(\n    configurableCorePool: ConfigurableCorePool,\n    callback?: (\n      configurableCorePool: ConfigurableCorePool,\n      returnValue: any\n    ) => void\n  ): Promise<string>;\n}\n"
  },
  {
    "path": "src/interface/TickView.ts",
    "content": "import { Tick } from \"../model/Tick\";\n\nexport type TickView = Pick<\n  Tick,\n  {\n    [K in keyof Tick]: Tick[K] extends Function ? never : K;\n  }[keyof Tick]\n>;\n"
  },
  {
    "path": "src/interface/Transition.ts",
    "content": "import { PoolStateView } from \"./PoolStateView\";\nimport { Record } from \"../entity/Record\";\n\nexport interface Transition {\n  getSource(): PoolStateView;\n  getTarget(): PoolStateView;\n  getRecord(): Record;\n}\n"
  },
  {
    "path": "src/interface/Visitable.ts",
    "content": "import { SimulatorVisitor } from \"./SimulatorVisitor\";\n\nexport interface Visitable {\n  accept(visitor: SimulatorVisitor): Promise<string>;\n}\n"
  },
  {
    "path": "src/interface/index.ts",
    "content": "export * from \"./ActionParams\";\nexport * from \"./ConfigurableCorePool\";\nexport * from \"./CorePoolView\";\nexport * from \"./PoolStateView\";\nexport * from \"./PositionView\";\nexport * from \"./TickView\";\nexport * from \"./SimulatorRoadmapManager\";\nexport * from \"./Transition\";\nexport * from \"./SimulationDataManager\";\n"
  },
  {
    "path": "src/manager/EventDBManager.ts",
    "content": "import { Knex, knex as knexBuilder } from \"knex\";\nimport { JSBIDeserializer } from \"../util/Serializer\";\nimport { LiquidityEvent } from \"../entity/LiquidityEvent\";\nimport { SwapEvent } from \"../entity/SwapEvent\";\nimport { DateConverter } from \"../util/DateConverter\";\nimport { EventType } from \"../enum/EventType\";\nimport { PoolConfig } from \"../model/PoolConfig\";\nimport { ZERO } from \"../enum/InternalConstants\";\nimport JSBI from \"jsbi\";\nimport * as log4js from \"log4js\";\n\nconst logger = log4js.getLogger(\"EventDBManager\");\n\nconst DATE_FORMAT: string = \"YYYY-MM-DD HH:mm:ss\";\n\ntype LiquidityEventRecord = {\n  id: number;\n  type: number;\n  msg_sender: string;\n  recipient: string;\n  liquidity: string;\n  amount0: string;\n  amount1: string;\n  tick_lower: number;\n  tick_upper: number;\n  block_number: number;\n  transaction_hash: string;\n  log_index: number;\n  date: string;\n  verified: boolean;\n};\n\ntype SwapEventRecord = {\n  id: number;\n  msg_sender: string;\n  recipient: string;\n  amount0: string;\n  amount1: string;\n  amount_specified: string;\n  sqrt_price_x96: string;\n  liquidity: string;\n  tick: number;\n  block_number: number;\n  transaction_hash: string;\n  log_index: number;\n  date: string;\n  verified: boolean;\n};\n\ntype PoolConfigRecord = {\n  id: number;\n  pool_config_id: string;\n  token0: string;\n  token1: string;\n  fee: number;\n  tick_spacing: number;\n  initial_sqrt_price_X96: string;\n  initialization_event_block_number: number;\n  latest_event_block_number: number;\n  latest_verified_swap_block_number: number;\n  latest_verified_burn_block_number: number;\n  timestamp: string;\n};\n\nexport class EventDBManager {\n  private knex: Knex;\n\n  private constructor(dbPath: string) {\n    const config: Knex.Config = {\n      client: \"sqlite3\",\n      connection: {\n        filename: dbPath,\n      },\n      useNullAsDefault: true,\n    };\n    this.knex = knexBuilder(config);\n  }\n\n  static async buildInstance(\n    dbPath: string = \":memory:\"\n  ): Promise<EventDBManager> {\n    let dbManager = new EventDBManager(dbPath);\n    await dbManager.initTables();\n    return dbManager;\n  }\n\n  initTables(): Promise<void> {\n    const knex = this.knex;\n    let tasks = [\n      knex.schema\n        .hasTable(\"pool_config\")\n        .then((exists: boolean) =>\n          !exists\n            ? knex.schema.createTable(\n                \"pool_config\",\n                function (t: Knex.TableBuilder) {\n                  t.increments(\"id\").primary();\n                  t.string(\"pool_config_id\", 32);\n                  t.string(\"token0\", 255);\n                  t.string(\"token1\", 255);\n                  t.integer(\"fee\");\n                  t.integer(\"tick_spacing\");\n                  t.string(\"initial_sqrt_price_X96\", 255);\n                  t.integer(\"initialization_event_block_number\");\n                  t.integer(\"latest_event_block_number\");\n                  t.integer(\"latest_verified_swap_block_number\");\n                  t.integer(\"latest_verified_burn_block_number\");\n                  t.text(\"timestamp\");\n                }\n              )\n            : Promise.resolve()\n        )\n        .then(() => this.migratePoolConfigTable())\n        .then(() => this.migrateEventTables()),\n      knex.schema.hasTable(\"liquidity_events\").then((exists: boolean) =>\n        !exists\n          ? knex.schema.createTable(\n              \"liquidity_events\",\n              function (t: Knex.TableBuilder) {\n                t.increments(\"id\").primary();\n                t.integer(\"type\");\n                t.string(\"msg_sender\", 255);\n                t.string(\"recipient\", 255);\n                t.string(\"liquidity\", 255);\n                t.string(\"amount0\", 255);\n                t.string(\"amount1\", 255);\n                t.integer(\"tick_lower\");\n                t.integer(\"tick_upper\");\n                t.integer(\"block_number\");\n                t.string(\"transaction_hash\", 255);\n                t.integer(\"log_index\");\n                t.text(\"date\");\n                t.boolean(\"verified\").defaultTo(false);\n                t.index([\"type\", \"block_number\"]);\n                t.index([\"type\", \"date\"]);\n                t.index([\"transaction_hash\", \"log_index\"]);\n              }\n            )\n          : Promise.resolve()\n      ),\n      knex.schema.hasTable(\"swap_events\").then((exists: boolean) =>\n        !exists\n          ? knex.schema.createTable(\n              \"swap_events\",\n              function (t: Knex.TableBuilder) {\n                t.increments(\"id\").primary();\n                t.string(\"msg_sender\", 255);\n                t.string(\"recipient\", 255);\n                t.string(\"amount0\", 255);\n                t.string(\"amount1\", 255);\n                t.string(\"amount_specified\", 255);\n                t.string(\"sqrt_price_x96\", 255);\n                t.string(\"liquidity\", 255);\n                t.integer(\"tick\");\n                t.integer(\"block_number\");\n                t.string(\"transaction_hash\", 255);\n                t.integer(\"log_index\");\n                t.text(\"date\");\n                t.boolean(\"verified\").defaultTo(false);\n                t.index([\"block_number\"]);\n                t.index([\"date\"]);\n                t.index([\"transaction_hash\", \"log_index\"]);\n              }\n            )\n          : Promise.resolve()\n      ),\n    ];\n    return Promise.all(tasks).then(() => Promise.resolve());\n  }\n\n  getPoolConfig(): Promise<PoolConfig | undefined> {\n    return this.readPoolConfig().then((res) =>\n      !res\n        ? Promise.resolve(undefined)\n        : Promise.resolve({\n            id: res.pool_config_id,\n            tickSpacing: res.tick_spacing,\n            token0: res.token0,\n            token1: res.token1,\n            fee: res.fee,\n          })\n    );\n  }\n\n  getInitializationEventBlockNumber(): Promise<number> {\n    return this.readPoolConfig().then((res) =>\n      !res\n        ? Promise.resolve(0)\n        : Promise.resolve(\n            null == res.initialization_event_block_number\n              ? 0\n              : res.initialization_event_block_number\n          )\n    );\n  }\n\n  getLatestEventBlockNumber(): Promise<number> {\n    return this.readPoolConfig().then((res) =>\n      !res ? Promise.resolve(0) : Promise.resolve(res.latest_event_block_number)\n    );\n  }\n\n  getInitialSqrtPriceX96(): Promise<JSBI> {\n    return this.readPoolConfig().then((res) =>\n      !res\n        ? Promise.resolve(ZERO)\n        : Promise.resolve(\n            null == res.initial_sqrt_price_X96\n              ? ZERO\n              : JSBI.BigInt(res.initial_sqrt_price_X96)\n          )\n    );\n  }\n\n  getLiquidityEventsByDate(\n    type: number,\n    startDate: string,\n    endDate: string\n  ): Promise<LiquidityEvent[]> {\n    return this.queryLiquidityEventsByDate(type, startDate, endDate).then(\n      (rows: LiquidityEventRecord[]) =>\n        Promise.resolve(\n          rows.map(\n            (row: LiquidityEventRecord): LiquidityEvent =>\n              this.deserializeLiquidityEvent(row)\n          )\n        )\n    );\n  }\n\n  getSwapEventsByDate(\n    startDate: string,\n    endDate: string\n  ): Promise<SwapEvent[]> {\n    return this.querySwapEventsByDate(startDate, endDate).then(\n      (rows: SwapEventRecord[]) =>\n        Promise.resolve(\n          rows.map(\n            (row: SwapEventRecord): SwapEvent => this.deserializeSwapEvent(row)\n          )\n        )\n    );\n  }\n\n  getLiquidityEventsByBlockNumber(\n    type: number,\n    fromBlock: number,\n    toBlock: number\n  ): Promise<LiquidityEvent[]> {\n    return this.queryLiquidityEventsByBlockNumber(\n      type,\n      fromBlock,\n      toBlock\n    ).then((rows: LiquidityEventRecord[]) =>\n      Promise.resolve(\n        rows.map(\n          (row: LiquidityEventRecord): LiquidityEvent =>\n            this.deserializeLiquidityEvent(row)\n        )\n      )\n    );\n  }\n\n  deleteLiquidityEventsByBlockNumber(\n    type: number,\n    fromBlock: number,\n    toBlock: number\n  ): Promise<void> {\n    return this.knex.transaction((trx) =>\n      this.getBuilderContext(\"liquidity_events\", trx)\n        .where(\"type\", type)\n        .andWhere(\"block_number\", \">=\", fromBlock)\n        .andWhere(\"block_number\", \"<=\", toBlock)\n        .del()\n    );\n  }\n\n  getSwapEventsByBlockNumber(\n    fromBlock: number,\n    toBlock: number\n  ): Promise<SwapEvent[]> {\n    return this.querySwapEventsByBlockNumber(fromBlock, toBlock).then(\n      (rows: SwapEventRecord[]) =>\n        Promise.resolve(\n          rows.map(\n            (row: SwapEventRecord): SwapEvent => this.deserializeSwapEvent(row)\n          )\n        )\n    );\n  }\n\n  getBurnEventsByTransactionHash(\n    transactionHash: string\n  ): Promise<LiquidityEvent[]> {\n    return this.queryBurnEventsByTransactionHash(transactionHash).then(\n      (rows: LiquidityEventRecord[]) =>\n        Promise.resolve(\n          rows.map((row: LiquidityEventRecord) =>\n            this.deserializeLiquidityEvent(row)\n          )\n        )\n    );\n  }\n\n  deleteSwapEventsByBlockNumber(\n    fromBlock: number,\n    toBlock: number\n  ): Promise<void> {\n    return this.knex.transaction((trx) =>\n      this.getBuilderContext(\"swap_events\", trx)\n        .andWhere(\"block_number\", \">=\", fromBlock)\n        .andWhere(\"block_number\", \"<=\", toBlock)\n        .del()\n    );\n  }\n\n  addPoolConfig(poolConfig: PoolConfig) {\n    return this.knex.transaction((trx) =>\n      this.insertPoolConfig(poolConfig, trx).then((ids) =>\n        Promise.resolve(ids[0])\n      )\n    );\n  }\n\n  addAmountSpecified(id: number, amountSpecified: string): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.updateAmountSpecified(id, amountSpecified, trx).then(\n        (updated_rows) => Promise.resolve(updated_rows)\n      )\n    );\n  }\n\n  addInitialSqrtPriceX96(initialSqrtPriceX96: string): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.updateInitialSqrtPriceX96(initialSqrtPriceX96, trx).then((ids) =>\n        Promise.resolve(ids[0])\n      )\n    );\n  }\n\n  saveLatestEventBlockNumber(latestEventBlockNumber: number): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.updateLatestEventBlockNumber(latestEventBlockNumber, trx).then(\n        (id) => Promise.resolve(id)\n      )\n    );\n  }\n\n  saveInitializationEventBlockNumber(\n    initializationEventBlockNumber: number\n  ): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.updateInitializationEventBlockNumber(\n        initializationEventBlockNumber,\n        trx\n      ).then((id) => Promise.resolve(id))\n    );\n  }\n\n  saveBurnEvent(\n    transactionHash: string,\n    logIndex: number,\n    liquidity: string,\n    amount0: string,\n    amount1: string,\n    tickLower: number,\n    tickUpper: number\n  ): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.updateBurnEvent(\n        transactionHash,\n        logIndex,\n        liquidity,\n        amount0,\n        amount1,\n        tickLower,\n        tickUpper,\n        trx\n      ).then((id) => Promise.resolve(id))\n    );\n  }\n\n  getLatestVerifiedSwapBlockNumber(): Promise<number> {\n    return this.readPoolConfig().then((res) =>\n      !res\n        ? Promise.resolve(0)\n        : Promise.resolve(\n            null == res.latest_verified_swap_block_number\n              ? 0\n              : res.latest_verified_swap_block_number\n          )\n    );\n  }\n\n  getLatestVerifiedBurnBlockNumber(): Promise<number> {\n    return this.readPoolConfig().then((res) =>\n      !res\n        ? Promise.resolve(0)\n        : Promise.resolve(\n            null == res.latest_verified_burn_block_number\n              ? 0\n              : res.latest_verified_burn_block_number\n          )\n    );\n  }\n\n  saveLatestVerifiedSwapBlockNumber(\n    latestVerifiedSwapBlockNumber: number\n  ): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.updateLatestVerifiedSwapBlockNumber(\n        latestVerifiedSwapBlockNumber,\n        trx\n      ).then((id) => Promise.resolve(id))\n    );\n  }\n\n  saveLatestVerifiedBurnBlockNumber(\n    latestVerifiedBurnBlockNumber: number\n  ): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.updateLatestVerifiedBurnBlockNumber(\n        latestVerifiedBurnBlockNumber,\n        trx\n      ).then((id) => Promise.resolve(id))\n    );\n  }\n\n  /**\n   * mark liquidity event as verified\n   * @param eventId event id\n   * @returns updated rows\n   */\n  markLiquidityEventAsVerified(eventId: number): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.getBuilderContext(\"liquidity_events\", trx)\n        .where(\"id\", eventId)\n        .update({ verified: true })\n    );\n  }\n\n  /**\n   * mark swap event as verified\n   * @param eventId event id\n   * @returns updated rows\n   */\n  markSwapEventAsVerified(eventId: number): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.getBuilderContext(\"swap_events\", trx)\n        .where(\"id\", eventId)\n        .update({ verified: true })\n    );\n  }\n\n  /**\n   * batch mark liquidity events as verified\n   * @param eventIds event ids\n   * @returns updated rows\n   */\n  markLiquidityEventsAsVerified(eventIds: number[]): Promise<number> {\n    if (eventIds.length === 0) {\n      return Promise.resolve(0);\n    }\n    return this.knex.transaction((trx) =>\n      this.getBuilderContext(\"liquidity_events\", trx)\n        .whereIn(\"id\", eventIds)\n        .update({ verified: true })\n    );\n  }\n\n  /**\n   * batch mark swap events as verified\n   * @param eventIds event ids\n   * @returns updated rows\n   */\n  markSwapEventsAsVerified(eventIds: number[]): Promise<number> {\n    if (eventIds.length === 0) {\n      return Promise.resolve(0);\n    }\n    return this.knex.transaction((trx) =>\n      this.getBuilderContext(\"swap_events\", trx)\n        .whereIn(\"id\", eventIds)\n        .update({ verified: true })\n    );\n  }\n\n  /**\n   * get unverified liquidity events\n   * @param type event type\n   * @param fromBlock start block\n   * @param toBlock end block\n   * @returns unverified liquidity events\n   */\n  getUnverifiedLiquidityEvents(\n    type: number,\n    fromBlock: number,\n    toBlock: number\n  ): Promise<LiquidityEvent[]> {\n    return this.getBuilderContext(\"liquidity_events\")\n      .where(\"type\", type)\n      .andWhere(\"block_number\", \">=\", fromBlock)\n      .andWhere(\"block_number\", \"<=\", toBlock)\n      .andWhere(\"verified\", false)\n      .then((rows: LiquidityEventRecord[]) =>\n        Promise.resolve(\n          rows.map(\n            (row: LiquidityEventRecord): LiquidityEvent =>\n              this.deserializeLiquidityEvent(row)\n          )\n        )\n      );\n  }\n\n  /**\n   * get unverified swap events\n   * @param fromBlock start block\n   * @param toBlock end block\n   * @returns unverified swap events\n   */\n  getUnverifiedSwapEvents(\n    fromBlock: number,\n    toBlock: number\n  ): Promise<SwapEvent[]> {\n    return this.getBuilderContext(\"swap_events\")\n      .where(\"block_number\", \">=\", fromBlock)\n      .andWhere(\"block_number\", \"<=\", toBlock)\n      .andWhere(\"verified\", false)\n      .then((rows: SwapEventRecord[]) =>\n        Promise.resolve(\n          rows.map(\n            (row: SwapEventRecord): SwapEvent => this.deserializeSwapEvent(row)\n          )\n        )\n      );\n  }\n\n  insertLiquidityEvent(\n    type: number,\n    msg_sender: string,\n    recipient: string,\n    liquidity: string,\n    amount0: string,\n    amount1: string,\n    tick_lower: number,\n    tick_upper: number,\n    block_number: number,\n    transaction_hash: string,\n    log_index: number,\n    date: Date\n  ): Promise<number> {\n    return this.knex\n      .transaction((trx) =>\n        this.getBuilderContext(\"liquidity_events\", trx).insert([\n          {\n            type,\n            msg_sender,\n            recipient,\n            liquidity,\n            amount0,\n            amount1,\n            tick_lower,\n            tick_upper,\n            block_number,\n            transaction_hash,\n            log_index,\n            date: DateConverter.formatDate(date, DATE_FORMAT),\n            verified: false,\n          },\n        ])\n      )\n      .then((ids) => Promise.resolve(ids[0]));\n  }\n\n  /**\n   * batch insert liquidity events\n   * @param events event array\n   * @param batchSize batch size, default 500\n   * @returns inserted record ids\n   */\n  async batchInsertLiquidityEvents(\n    events: Array<{\n      type: number;\n      msg_sender: string;\n      recipient: string;\n      liquidity: string;\n      amount0: string;\n      amount1: string;\n      tick_lower: number;\n      tick_upper: number;\n      block_number: number;\n      transaction_hash: string;\n      log_index: number;\n      date: Date;\n    }>,\n    batchSize: number = 500\n  ): Promise<number[]> {\n    if (events.length === 0) {\n      return Promise.resolve([]);\n    }\n\n    // if the number of events is less than or equal to the batch size, insert directly\n    if (events.length <= batchSize) {\n      const records = events.map((event) => ({\n        type: event.type,\n        msg_sender: event.msg_sender,\n        recipient: event.recipient,\n        liquidity: event.liquidity,\n        amount0: event.amount0,\n        amount1: event.amount1,\n        tick_lower: event.tick_lower,\n        tick_upper: event.tick_upper,\n        block_number: event.block_number,\n        transaction_hash: event.transaction_hash,\n        log_index: event.log_index,\n        date: DateConverter.formatDate(event.date, DATE_FORMAT),\n        verified: false,\n      }));\n\n      logger.info(\n        `Inserted ${records.length} liquidity events, type: ${events[0].type}`\n      );\n\n      return this.knex\n        .transaction((trx) =>\n          this.getBuilderContext(\"liquidity_events\", trx).insert(records)\n        )\n        .then((ids) => Promise.resolve(ids));\n    }\n\n    // batch process large data\n    const allIds: number[] = [];\n    const totalBatches = Math.ceil(events.length / batchSize);\n\n    for (let i = 0; i < totalBatches; i++) {\n      const startIndex = i * batchSize;\n      const endIndex = Math.min(startIndex + batchSize, events.length);\n      const batch = events.slice(startIndex, endIndex);\n\n      const records = batch.map((event) => ({\n        type: event.type,\n        msg_sender: event.msg_sender,\n        recipient: event.recipient,\n        liquidity: event.liquidity,\n        amount0: event.amount0,\n        amount1: event.amount1,\n        tick_lower: event.tick_lower,\n        tick_upper: event.tick_upper,\n        block_number: event.block_number,\n        transaction_hash: event.transaction_hash,\n        log_index: event.log_index,\n        date: DateConverter.formatDate(event.date, DATE_FORMAT),\n        verified: false,\n      }));\n\n      const batchIds = await this.knex.transaction((trx) =>\n        this.getBuilderContext(\"liquidity_events\", trx).insert(records)\n      );\n\n      allIds.push(...batchIds);\n    }\n\n    logger.info(\n      `Inserted ${events.length} liquidity events, type: ${events[0].type}, batchSize: ${batchSize}, totalBatches: ${totalBatches}, allIds: ${allIds.length}`\n    );\n\n    return Promise.resolve(allIds);\n  }\n\n  insertSwapEvent(\n    msg_sender: string,\n    recipient: string,\n    amount0: string,\n    amount1: string,\n    sqrt_price_x96: string,\n    liquidity: string,\n    tick: number,\n    block_number: number,\n    transaction_hash: string,\n    log_index: number,\n    date: Date\n  ): Promise<number> {\n    return this.knex.transaction((trx) =>\n      this.getBuilderContext(\"swap_events\", trx).insert([\n        {\n          msg_sender,\n          recipient,\n          amount0,\n          amount1,\n          amount_specified: undefined,\n          sqrt_price_x96,\n          liquidity,\n          tick,\n          block_number,\n          transaction_hash,\n          log_index,\n          date: DateConverter.formatDate(date, DATE_FORMAT),\n          verified: false,\n        },\n      ])\n    );\n  }\n\n  /**\n   * batch insert swap events\n   * @param events event array\n   * @param batchSize batch size, default 500\n   * @returns inserted record ids\n   */\n  async batchInsertSwapEvents(\n    events: Array<{\n      msg_sender: string;\n      recipient: string;\n      amount0: string;\n      amount1: string;\n      sqrt_price_x96: string;\n      liquidity: string;\n      tick: number;\n      block_number: number;\n      transaction_hash: string;\n      log_index: number;\n      date: Date;\n    }>,\n    batchSize: number = 500\n  ): Promise<number[]> {\n    if (events.length === 0) {\n      return Promise.resolve([]);\n    }\n\n    // if the number of events is less than or equal to the batch size, insert directly\n    if (events.length <= batchSize) {\n      const records = events.map((event) => ({\n        msg_sender: event.msg_sender,\n        recipient: event.recipient,\n        amount0: event.amount0,\n        amount1: event.amount1,\n        amount_specified: undefined,\n        sqrt_price_x96: event.sqrt_price_x96,\n        liquidity: event.liquidity,\n        tick: event.tick,\n        block_number: event.block_number,\n        transaction_hash: event.transaction_hash,\n        log_index: event.log_index,\n        date: DateConverter.formatDate(event.date, DATE_FORMAT),\n        verified: false,\n      }));\n\n      logger.info(`Inserted ${records.length} swap events`);\n\n      return this.knex\n        .transaction((trx) =>\n          this.getBuilderContext(\"swap_events\", trx).insert(records)\n        )\n        .then((ids) => Promise.resolve(ids));\n    }\n\n    // batch process large data\n    const allIds: number[] = [];\n    const totalBatches = Math.ceil(events.length / batchSize);\n\n    for (let i = 0; i < totalBatches; i++) {\n      const startIndex = i * batchSize;\n      const endIndex = Math.min(startIndex + batchSize, events.length);\n      const batch = events.slice(startIndex, endIndex);\n\n      const records = batch.map((event) => ({\n        msg_sender: event.msg_sender,\n        recipient: event.recipient,\n        amount0: event.amount0,\n        amount1: event.amount1,\n        amount_specified: undefined,\n        sqrt_price_x96: event.sqrt_price_x96,\n        liquidity: event.liquidity,\n        tick: event.tick,\n        block_number: event.block_number,\n        transaction_hash: event.transaction_hash,\n        log_index: event.log_index,\n        date: DateConverter.formatDate(event.date, DATE_FORMAT),\n        verified: false,\n      }));\n\n      const batchIds = await this.knex.transaction((trx) =>\n        this.getBuilderContext(\"swap_events\", trx).insert(records)\n      );\n\n      allIds.push(...batchIds);\n    }\n\n    logger.info(\n      `Inserted ${events.length} swap events, batchSize: ${batchSize}, totalBatches: ${totalBatches}, allIds: ${allIds.length}`\n    );\n\n    return Promise.resolve(allIds);\n  }\n\n  close(): Promise<void> {\n    return this.knex.destroy();\n  }\n\n  private readPoolConfig(\n    trx?: Knex.Transaction\n  ): Promise<PoolConfigRecord | undefined> {\n    return this.getBuilderContext(\"pool_config\", trx).first();\n  }\n\n  private queryLiquidityEventsByDate(\n    type: number,\n    startDate: string,\n    endDate: string,\n    trx?: Knex.Transaction\n  ): Promise<LiquidityEventRecord[]> {\n    return this.getBuilderContext(\"liquidity_events\", trx)\n      .where(\"type\", type)\n      .andWhere(\"date\", \">=\", startDate)\n      .andWhere(\"date\", \"<\", endDate);\n  }\n\n  private querySwapEventsByDate(\n    startDate: string,\n    endDate: string,\n    trx?: Knex.Transaction\n  ): Promise<SwapEventRecord[]> {\n    return this.getBuilderContext(\"swap_events\", trx)\n      .andWhere(\"date\", \">=\", startDate)\n      .andWhere(\"date\", \"<\", endDate);\n  }\n\n  private queryLiquidityEventsByBlockNumber(\n    type: number,\n    fromBlock: number,\n    toBlock: number,\n    trx?: Knex.Transaction\n  ): Promise<LiquidityEventRecord[]> {\n    return this.getBuilderContext(\"liquidity_events\", trx)\n      .where(\"type\", type)\n      .andWhere(\"block_number\", \">=\", fromBlock)\n      .andWhere(\"block_number\", \"<=\", toBlock);\n  }\n\n  private querySwapEventsByBlockNumber(\n    fromBlock: number,\n    toBlock: number,\n    trx?: Knex.Transaction\n  ): Promise<SwapEventRecord[]> {\n    return this.getBuilderContext(\"swap_events\", trx)\n      .andWhere(\"block_number\", \">=\", fromBlock)\n      .andWhere(\"block_number\", \"<=\", toBlock);\n  }\n\n  private queryBurnEventsByTransactionHash(\n    transactionHash: string,\n    trx?: Knex.Transaction\n  ): Promise<LiquidityEventRecord[]> {\n    return this.getBuilderContext(\"liquidity_events\", trx)\n      .where(\"transaction_hash\", transactionHash)\n      .andWhere(\"type\", EventType.BURN)\n      .andWhere(\"verified\", false);\n  }\n\n  private insertPoolConfig(\n    poolConfig: PoolConfig,\n    trx?: Knex.Transaction\n  ): Promise<Array<number>> {\n    return this.getBuilderContext(\"pool_config\", trx).insert([\n      {\n        pool_config_id: poolConfig.id,\n        token0: poolConfig.token0,\n        token1: poolConfig.token1,\n        fee: poolConfig.fee,\n        tick_spacing: poolConfig.tickSpacing,\n        initial_sqrt_price_X96: undefined,\n        latest_event_block_number: 0,\n        latest_verified_swap_block_number: 0,\n        latest_verified_burn_block_number: 0,\n        timestamp: DateConverter.formatDate(new Date(), DATE_FORMAT),\n      },\n    ]);\n  }\n\n  private updateAmountSpecified(\n    id: number,\n    amountSpecified: string,\n    trx?: Knex.Transaction\n  ): Promise<number> {\n    return this.getBuilderContext(\"swap_events\", trx)\n      .update({\n        amount_specified: amountSpecified,\n        verified: true,\n      })\n      .where(\"id\", id);\n  }\n\n  private updateInitialSqrtPriceX96(\n    initialSqrtPriceX96: string,\n    trx?: Knex.Transaction\n  ): Promise<Array<number>> {\n    return this.getBuilderContext(\"pool_config\", trx)\n      .update(\"initial_sqrt_price_X96\", initialSqrtPriceX96)\n      .where(\"id\", 1);\n  }\n\n  private updateLatestEventBlockNumber(\n    latestEventBlockNumber: number,\n    trx?: Knex.Transaction\n  ): Promise<number> {\n    return this.getBuilderContext(\"pool_config\", trx)\n      .update(\"latest_event_block_number\", latestEventBlockNumber)\n      .where(\"id\", 1);\n  }\n\n  private updateInitializationEventBlockNumber(\n    initializationEventBlockNumber: number,\n    trx?: Knex.Transaction\n  ): Promise<number> {\n    return this.getBuilderContext(\"pool_config\", trx)\n      .update(\n        \"initialization_event_block_number\",\n        initializationEventBlockNumber\n      )\n      .where(\"id\", 1);\n  }\n\n  private updateLatestVerifiedSwapBlockNumber(\n    latestVerifiedSwapBlockNumber: number,\n    trx?: Knex.Transaction\n  ): Promise<number> {\n    return this.getBuilderContext(\"pool_config\", trx)\n      .update(\n        \"latest_verified_swap_block_number\",\n        latestVerifiedSwapBlockNumber\n      )\n      .where(\"id\", 1);\n  }\n\n  private updateLatestVerifiedBurnBlockNumber(\n    latestVerifiedBurnBlockNumber: number,\n    trx?: Knex.Transaction\n  ): Promise<number> {\n    return this.getBuilderContext(\"pool_config\", trx)\n      .update(\n        \"latest_verified_burn_block_number\",\n        latestVerifiedBurnBlockNumber\n      )\n      .where(\"id\", 1);\n  }\n\n  private updateBurnEvent(\n    transactionHash: string,\n    logIndex: number,\n    liquidity: string,\n    amount0: string,\n    amount1: string,\n    tickLower: number,\n    tickUpper: number,\n    trx?: Knex.Transaction\n  ): Promise<number> {\n    return this.getBuilderContext(\"liquidity_events\", trx)\n      .update({\n        liquidity,\n        amount0,\n        amount1,\n        tick_lower: tickLower,\n        tick_upper: tickUpper,\n        verified: true,\n      })\n      .where(\"transaction_hash\", transactionHash)\n      .andWhere(\"type\", EventType.BURN)\n      .andWhere(\"verified\", false)\n      .orderBy(\"log_index\", \"asc\")\n      .limit(1);\n    // because collect from subgraph is different from burn from RPC, we can't use logIndex to update\n    // .andWhere(\"log_index\", logIndex)\n  }\n\n  private async migratePoolConfigTable(): Promise<void> {\n    try {\n      // check if new columns are needed\n      const hasVerifiedSwapColumn = await this.knex.schema.hasColumn(\n        \"pool_config\",\n        \"latest_verified_swap_block_number\"\n      );\n      const hasVerifiedBurnColumn = await this.knex.schema.hasColumn(\n        \"pool_config\",\n        \"latest_verified_burn_block_number\"\n      );\n\n      if (!hasVerifiedSwapColumn) {\n        await this.knex.schema.alterTable(\"pool_config\", (table) => {\n          table.integer(\"latest_verified_swap_block_number\").defaultTo(0);\n        });\n        logger.info(\n          \"Added latest_verified_swap_block_number column to pool_config table\"\n        );\n      }\n\n      if (!hasVerifiedBurnColumn) {\n        await this.knex.schema.alterTable(\"pool_config\", (table) => {\n          table.integer(\"latest_verified_burn_block_number\").defaultTo(0);\n        });\n        logger.info(\n          \"Added latest_verified_burn_block_number column to pool_config table\"\n        );\n      }\n\n      // update existing records with default values\n      await this.knex(\"pool_config\")\n        .whereNull(\"latest_verified_swap_block_number\")\n        .update({ latest_verified_swap_block_number: 0 });\n\n      await this.knex(\"pool_config\")\n        .whereNull(\"latest_verified_burn_block_number\")\n        .update({ latest_verified_burn_block_number: 0 });\n\n      logger.info(\"Migration completed successfully\");\n    } catch (error) {\n      logger.error(\"Migration failed:\", error);\n      throw error;\n    }\n  }\n\n  private async migrateEventTables(): Promise<void> {\n    try {\n      // check if liquidity_events table has transaction_hash_log_index index\n      const liquidityEventsIndexes = await this.knex.raw(\n        \"PRAGMA index_list(liquidity_events)\"\n      );\n      const hasLiquidityEventsIndex = liquidityEventsIndexes.some(\n        (index: any) =>\n          index.name === \"liquidity_events_transaction_hash_log_index\"\n      );\n\n      if (!hasLiquidityEventsIndex) {\n        await this.knex.schema.alterTable(\"liquidity_events\", (table) => {\n          table.index(\n            [\"transaction_hash\", \"log_index\"],\n            \"liquidity_events_transaction_hash_log_index\"\n          );\n        });\n        logger.info(\n          \"Added transaction_hash_log_index index to liquidity_events table\"\n        );\n      }\n\n      // check if swap_events table has transaction_hash_log_index index\n      const swapEventsIndexes = await this.knex.raw(\n        \"PRAGMA index_list(swap_events)\"\n      );\n      const hasSwapEventsIndex = swapEventsIndexes.some(\n        (index: any) => index.name === \"swap_events_transaction_hash_log_index\"\n      );\n\n      if (!hasSwapEventsIndex) {\n        await this.knex.schema.alterTable(\"swap_events\", (table) => {\n          table.index(\n            [\"transaction_hash\", \"log_index\"],\n            \"swap_events_transaction_hash_log_index\"\n          );\n        });\n        logger.info(\n          \"Added transaction_hash_log_index index to swap_events table\"\n        );\n      }\n\n      // check and add verified column to liquidity_events table\n      const hasLiquidityEventsVerifiedColumn = await this.knex.schema.hasColumn(\n        \"liquidity_events\",\n        \"verified\"\n      );\n\n      if (!hasLiquidityEventsVerifiedColumn) {\n        await this.knex.schema.alterTable(\"liquidity_events\", (table) => {\n          table.boolean(\"verified\").defaultTo(false);\n        });\n        logger.info(\"Added verified column to liquidity_events table\");\n      }\n\n      // check and add verified column to swap_events table\n      const hasSwapEventsVerifiedColumn = await this.knex.schema.hasColumn(\n        \"swap_events\",\n        \"verified\"\n      );\n\n      if (!hasSwapEventsVerifiedColumn) {\n        await this.knex.schema.alterTable(\"swap_events\", (table) => {\n          table.boolean(\"verified\").defaultTo(false);\n        });\n        logger.info(\"Added verified column to swap_events table\");\n      }\n\n      logger.info(\"Event tables migration completed successfully\");\n    } catch (error) {\n      logger.error(\"Event tables migration failed:\", error);\n      throw error;\n    }\n  }\n\n  private deserializeLiquidityEvent(\n    event: LiquidityEventRecord\n  ): LiquidityEvent {\n    return {\n      id: event.id,\n      type: event.type,\n      msgSender: event.msg_sender,\n      recipient: event.recipient,\n      liquidity: JSBIDeserializer(event.liquidity),\n      amount0: JSBIDeserializer(event.amount0),\n      amount1: JSBIDeserializer(event.amount1),\n      tickLower: event.tick_lower,\n      tickUpper: event.tick_upper,\n      blockNumber: event.block_number,\n      transactionHash: event.transaction_hash,\n      logIndex: event.log_index,\n      date: DateConverter.parseDate(event.date),\n      verified: event.verified || false,\n    };\n  }\n\n  private deserializeSwapEvent(event: SwapEventRecord): SwapEvent {\n    return {\n      id: event.id,\n      type: EventType.SWAP,\n      msgSender: event.msg_sender,\n      recipient: event.recipient,\n      amount0: JSBIDeserializer(event.amount0),\n      amount1: JSBIDeserializer(event.amount1),\n      amountSpecified: JSBIDeserializer(event.amount_specified),\n      sqrtPriceX96: JSBIDeserializer(event.sqrt_price_x96),\n      liquidity: JSBIDeserializer(event.liquidity),\n      tick: event.tick,\n      blockNumber: event.block_number,\n      transactionHash: event.transaction_hash,\n      logIndex: event.log_index,\n      date: DateConverter.parseDate(event.date),\n      verified: event.verified || false,\n    };\n  }\n\n  private getBuilderContext(\n    tableName: string,\n    trx?: Knex.Transaction\n  ): Knex.QueryBuilder {\n    return trx ? trx(tableName) : this.knex(tableName);\n  }\n}\n"
  },
  {
    "path": "src/manager/PositionManager.ts",
    "content": "import { Position } from \"../model/Position\";\nimport { jsonMapMember, jsonObject } from \"typedjson\";\nimport JSBI from \"jsbi\";\nimport assert from \"assert\";\nimport { ZERO } from \"../enum/InternalConstants\";\nimport { PositionView } from \"../interface/PositionView\";\n\n@jsonObject\nexport class PositionManager {\n  @jsonMapMember(String, Position, { name: \"positions_json\" })\n  private positions: Map<string, Position>;\n\n  // private owner_positions: Map<string, Map<string, Position>>;\n\n  constructor(positions: Map<string, Position> = new Map()) {\n    this.positions = positions;\n    // this.owner_positions = new Map();\n  }\n\n  static getKey(owner: string, tickLower: number, tickUpper: number): string {\n    // We might need a fancier hash function here\n    // but for now, I think this will do, and it's more verbose:\n    return owner + \"_\" + tickLower.toString() + \"_\" + tickUpper.toString();\n  }\n\n  static extractFromKey(key: string): {\n    owner: string;\n    tickLower: number;\n    tickUpper: number;\n  } {\n    const [owner, tickLower, tickUpper] = key.split(\"_\");\n    return {\n      owner: owner,\n      tickLower: parseInt(tickLower),\n      tickUpper: parseInt(tickUpper),\n    };\n  }\n\n  set(owner: string, key: string, position: Position) {\n    this.positions.set(key, position);\n\n    // if (!this.owner_positions.has(owner)) {\n    //   this.owner_positions.set(owner, new Map());\n    // }\n    // this.owner_positions.get(owner)!.set(key, position);\n  }\n\n  clear(key: string) {\n    if (this.positions.has(key)) this.positions.delete(key);\n  }\n\n  // getPositionsByOwner(owner: string): Map<string, Position> {\n  //   if (!this.owner_positions.has(owner)) {\n  //     this.owner_positions.set(owner, new Map());\n  //   }\n  //   return this.owner_positions.get(owner)!;\n  // }\n\n  getPositionAndInitIfAbsent(\n    owner: string,\n    tickLower: number,\n    tickUpper: number\n  ): Position {\n    const key = PositionManager.getKey(owner, tickLower, tickUpper);\n    if (this.positions.has(key)) return this.positions.get(key)!;\n    const newPosition = new Position();\n    this.set(owner, key, newPosition);\n    return newPosition;\n  }\n\n  getPositionReadonly(\n    owner: string,\n    tickLower: number,\n    tickUpper: number\n  ): PositionView {\n    const key = PositionManager.getKey(owner, tickLower, tickUpper);\n    if (this.positions.has(key)) return this.positions.get(key)!;\n    return new Position();\n  }\n\n  collectPosition(\n    owner: string,\n    tickLower: number,\n    tickUpper: number,\n    amount0Requested: JSBI,\n    amount1Requested: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    assert(\n      JSBI.greaterThanOrEqual(amount0Requested, ZERO) &&\n        JSBI.greaterThanOrEqual(amount1Requested, ZERO),\n      \"amounts requested should be positive\"\n    );\n    const key = PositionManager.getKey(owner, tickLower, tickUpper);\n    if (this.positions.has(key)) {\n      const positionToCollect = this.positions.get(key)!;\n      const amount0 = JSBI.greaterThan(\n        amount0Requested,\n        positionToCollect.tokensOwed0\n      )\n        ? positionToCollect.tokensOwed0\n        : amount0Requested;\n      const amount1 = JSBI.greaterThan(\n        amount1Requested,\n        positionToCollect.tokensOwed1\n      )\n        ? positionToCollect.tokensOwed1\n        : amount1Requested;\n      if (JSBI.greaterThan(amount0, ZERO) || JSBI.greaterThan(amount1, ZERO)) {\n        positionToCollect.updateBurn(\n          JSBI.subtract(positionToCollect.tokensOwed0, amount0),\n          JSBI.subtract(positionToCollect.tokensOwed1, amount1)\n        );\n        if (positionToCollect.isEmpty()) this.clear(key);\n      }\n      return { amount0: amount0, amount1: amount1 };\n    }\n    return { amount0: ZERO, amount1: ZERO };\n  }\n}\n"
  },
  {
    "path": "src/manager/SQLiteSimulationDataManager.ts",
    "content": "import JSBI from \"jsbi\";\nimport { SimulationDataManager } from \"../interface/SimulationDataManager\";\nimport { PoolState } from \"../model/PoolState\";\nimport { TickManager } from \"./TickManager\";\nimport { PositionManager } from \"./PositionManager\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { Roadmap } from \"../model/Roadmap\";\nimport { SnapshotProfile } from \"../entity/SnapshotProfile\";\nimport { PoolConfig } from \"../model/PoolConfig\";\nimport { Knex, knex as knexBuilder } from \"knex\";\nimport {\n  Serializer,\n  JSBISerializer,\n  JSBIDeserializer,\n  NumberArraySerializer,\n  NumberArrayDeserializer,\n} from \"../util/Serializer\";\nimport { DateConverter } from \"../util/DateConverter\";\n\nconst DATE_FORMAT: string = \"YYYY-MM-DD HH:mm:ss.SSS\";\n\ntype RoadmapRecord = {\n  id: number;\n  roadmapId: string;\n  description: string;\n  snapshots: string;\n  timestamp: string;\n};\n\ntype SnapshotRecord = {\n  id: number;\n  poolConfigId: string;\n  snapshotId: string;\n  description: string;\n  token0Balance: string;\n  token1Balance: string;\n  sqrtPriceX96: string;\n  liquidity: string;\n  tickCurrent: number;\n  feeGrowthGlobal0X128: string;\n  feeGrowthGlobal1X128: string;\n  tickManager: string;\n  positionManager: string;\n  timestamp: string;\n};\n\ntype PoolConfigRecord = {\n  id: number;\n  poolConfigId: string;\n  token0: string;\n  token1: string;\n  fee: number;\n  tickSpacing: number;\n  timestamp: string;\n};\n\nexport class SQLiteSimulationDataManager implements SimulationDataManager {\n  private knex: Knex;\n\n  private constructor(dbPath: string) {\n    const config: Knex.Config = {\n      client: \"sqlite3\",\n      connection: {\n        filename: dbPath, //:memory:\n      },\n      // sqlite does not support inserting default values. Set the `useNullAsDefault` flag to hide the warning.\n      useNullAsDefault: true,\n    };\n    this.knex = knexBuilder(config);\n  }\n\n  static async buildInstance(\n    dbPath: string = \":memory:\"\n  ): Promise<SimulationDataManager> {\n    let dbManager = new SQLiteSimulationDataManager(dbPath);\n    await dbManager.initTables();\n    return dbManager;\n  }\n\n  initTables(): Promise<void> {\n    const knex = this.knex;\n    let tasks = [\n      knex.schema.hasTable(\"poolConfig\").then((exists: boolean) =>\n        !exists\n          ? knex.schema.createTable(\n              \"poolConfig\",\n              function (t: Knex.TableBuilder) {\n                t.increments(\"id\").primary();\n                t.string(\"poolConfigId\", 32);\n                t.string(\"token0\", 255);\n                t.string(\"token1\", 255);\n                t.integer(\"fee\");\n                t.integer(\"tickSpacing\");\n                t.text(\"timestamp\");\n              }\n            )\n          : Promise.resolve()\n      ),\n      knex.schema.hasTable(\"snapshot\").then((exists: boolean) =>\n        !exists\n          ? knex.schema.createTable(\n              \"snapshot\",\n              function (t: Knex.TableBuilder) {\n                t.increments(\"id\").primary();\n                t.string(\"snapshotId\", 32);\n                t.string(\"poolConfigId\", 32);\n                t.string(\"description\", 255);\n                t.string(\"token0Balance\", 255);\n                t.string(\"token1Balance\", 255);\n                t.string(\"sqrtPriceX96\", 255);\n                t.string(\"liquidity\", 255);\n                t.integer(\"tickCurrent\");\n                t.string(\"feeGrowthGlobal0X128\", 255);\n                t.string(\"feeGrowthGlobal1X128\", 255);\n                t.string(\"tickManager\");\n                t.string(\"positionManager\");\n                t.text(\"timestamp\");\n              }\n            )\n          : Promise.resolve()\n      ),\n      knex.schema.hasTable(\"roadmap\").then((exists: boolean) =>\n        !exists\n          ? knex.schema.createTable(\"roadmap\", function (t: Knex.TableBuilder) {\n              t.increments(\"id\").primary();\n              t.string(\"roadmapId\", 32);\n              t.string(\"description\", 255);\n              t.string(\"snapshots\", 255);\n              t.text(\"timestamp\");\n            })\n          : Promise.resolve()\n      ),\n    ];\n    return Promise.all(tasks).then(() => Promise.resolve());\n  }\n\n  persistRoadmap(roadmap: Roadmap): Promise<number> {\n    return this.knex\n      .transaction((trx) =>\n        this.insertRoadmap(\n          roadmap.id,\n          roadmap.description,\n          roadmap.snapshots,\n          roadmap.timestamp,\n          trx\n        )\n      )\n      .then((ids) => Promise.resolve(ids[0]));\n  }\n\n  persistSnapshot(poolState: PoolState): Promise<number> {\n    let poolConfigId = poolState.poolConfig.id;\n    let snapshot = poolState.snapshot!;\n    return this.knex.transaction((trx) =>\n      this.readPoolConfig(poolConfigId, trx)\n        .then((poolConfig: PoolConfigRecord | undefined) =>\n          !poolConfig\n            ? this.insertPoolConfig(poolState.poolConfig, trx)\n            : Promise.resolve([])\n        )\n        .then(() =>\n          this.insertSnapshot(\n            snapshot.id,\n            poolConfigId,\n            snapshot.description,\n            snapshot.token0Balance,\n            snapshot.token1Balance,\n            snapshot.sqrtPriceX96,\n            snapshot.liquidity,\n            snapshot.tickCurrent,\n            snapshot.feeGrowthGlobal0X128,\n            snapshot.feeGrowthGlobal1X128,\n            snapshot.tickManager,\n            snapshot.positionManager,\n            snapshot.timestamp,\n            trx\n          )\n        )\n        .then((ids) => Promise.resolve(ids[0]))\n    );\n  }\n\n  getSnapshotProfiles(): Promise<SnapshotProfile[]> {\n    return this.readSnapshotProfiles().then((rows: SnapshotRecord[]) =>\n      Promise.resolve(\n        rows.map((row: SnapshotRecord): SnapshotProfile => {\n          return {\n            id: row.snapshotId,\n            description: row.description,\n          };\n        })\n      )\n    );\n  }\n\n  getSnapshots(snapshotIds: number[]): Promise<Snapshot[]> {\n    let snapshotRecords: SnapshotRecord[];\n    return this.readSnapshots(snapshotIds)\n      .then((snapshots: SnapshotRecord[]) => {\n        if (snapshots.length === 0) return Promise.reject(undefined);\n        snapshotRecords = snapshots;\n        return this.getPoolConfig(snapshots[0].poolConfigId);\n      })\n      .then((poolConfig: PoolConfig | undefined) => {\n        return !poolConfig\n          ? Promise.reject(\n              new Error(\n                \"Pool config record is missing, please check your db file.\"\n              )\n            )\n          : Promise.resolve(\n              snapshotRecords.map((snapshot: SnapshotRecord) =>\n                this.deserializeSnapshot(snapshot, poolConfig)\n              )\n            );\n      })\n      .catch((err) => (err ? Promise.reject(err) : Promise.resolve([])));\n  }\n\n  getSnapshot(snapshotId: string): Promise<Snapshot | undefined> {\n    let snapshotRecord: SnapshotRecord;\n    return this.readSnapshot(snapshotId)\n      .then((snapshot: SnapshotRecord | undefined) => {\n        if (!snapshot) return Promise.reject(undefined);\n        snapshotRecord = snapshot;\n        return this.getPoolConfig(snapshot.poolConfigId);\n      })\n      .then((poolConfig: PoolConfig | undefined) =>\n        !poolConfig\n          ? Promise.reject(\n              new Error(\n                \"Pool config record is missing, please check your db file.\"\n              )\n            )\n          : Promise.resolve(\n              this.deserializeSnapshot(snapshotRecord, poolConfig)\n            )\n      )\n      .catch((err) => (err ? Promise.reject(err) : Promise.resolve(undefined)));\n  }\n\n  getPoolConfig(poolConfigId: string): Promise<PoolConfig | undefined> {\n    return this.readPoolConfig(poolConfigId).then((res) =>\n      !res\n        ? Promise.resolve(undefined)\n        : Promise.resolve({\n            id: res.poolConfigId,\n            tickSpacing: res.tickSpacing,\n            token0: res.token0,\n            token1: res.token1,\n            fee: res.fee,\n          })\n    );\n  }\n\n  getRoadmap(roadmapId: string): Promise<Roadmap | undefined> {\n    return this.readRoadmap(roadmapId).then((res) =>\n      !res\n        ? Promise.resolve(undefined)\n        : Promise.resolve({\n            id: res.roadmapId,\n            description: res.description,\n            snapshots: NumberArrayDeserializer(res.snapshots),\n            timestamp: DateConverter.parseDate(res.timestamp),\n          })\n    );\n  }\n\n  close(): Promise<void> {\n    return this.knex.destroy();\n  }\n\n  private readSnapshot(\n    snapshotId: string,\n    trx?: Knex.Transaction\n  ): Promise<SnapshotRecord | undefined> {\n    return this.getBuilderContext(\"snapshot\", trx)\n      .where(\"snapshotId\", snapshotId)\n      .first();\n  }\n\n  private readSnapshots(\n    snapshotIds: number[],\n    trx?: Knex.Transaction\n  ): Promise<SnapshotRecord[]> {\n    return this.getBuilderContext(\"snapshot\", trx).whereIn(\"id\", snapshotIds);\n  }\n\n  private readSnapshotProfiles(\n    trx?: Knex.Transaction\n  ): Promise<SnapshotRecord[]> {\n    return this.getBuilderContext(\"snapshot\", trx)\n      .select(\"snapshotId\")\n      .select(\"description\");\n  }\n\n  private insertSnapshot(\n    snapshotId: string,\n    poolConfigId: string,\n    description: string,\n    token0Balance: JSBI,\n    token1Balance: JSBI,\n    sqrtPriceX96: JSBI,\n    liquidity: JSBI,\n    tickCurrent: number,\n    feeGrowthGlobal0X128: JSBI,\n    feeGrowthGlobal1X128: JSBI,\n    tickManager: TickManager,\n    positionManager: PositionManager,\n    timestamp: Date,\n    trx?: Knex.Transaction\n  ): Promise<Array<number>> {\n    return this.getBuilderContext(\"snapshot\", trx).insert([\n      {\n        snapshotId,\n        poolConfigId,\n        description,\n        token0Balance: JSBISerializer(token0Balance),\n        token1Balance: JSBISerializer(token1Balance),\n        sqrtPriceX96: JSBISerializer(sqrtPriceX96),\n        liquidity: JSBISerializer(liquidity),\n        tickCurrent,\n        feeGrowthGlobal0X128: JSBISerializer(feeGrowthGlobal0X128),\n        feeGrowthGlobal1X128: JSBISerializer(feeGrowthGlobal1X128),\n        tickManager: Serializer.serialize(TickManager, tickManager),\n        positionManager: Serializer.serialize(PositionManager, positionManager),\n        timestamp: DateConverter.formatDate(timestamp, DATE_FORMAT),\n      },\n    ]);\n  }\n\n  private insertRoadmap(\n    roadmapId: string,\n    description: string,\n    snapshots: number[],\n    timestamp: Date,\n    trx?: Knex.Transaction\n  ): Promise<Array<number>> {\n    return this.getBuilderContext(\"roadmap\", trx).insert([\n      {\n        roadmapId,\n        description,\n        snapshots: NumberArraySerializer(snapshots),\n        timestamp: DateConverter.formatDate(timestamp, DATE_FORMAT),\n      },\n    ]);\n  }\n\n  private readRoadmap(\n    roadmapId: string,\n    trx?: Knex.Transaction\n  ): Promise<RoadmapRecord | undefined> {\n    return this.getBuilderContext(\"roadmap\", trx)\n      .where(\"roadmapId\", roadmapId)\n      .first();\n  }\n\n  private readPoolConfig(\n    poolConfigId: string,\n    trx?: Knex.Transaction\n  ): Promise<PoolConfigRecord | undefined> {\n    return this.getBuilderContext(\"poolConfig\", trx)\n      .where(\"poolConfigId\", poolConfigId)\n      .first();\n  }\n\n  private insertPoolConfig(\n    poolConfig: PoolConfig,\n    trx?: Knex.Transaction\n  ): Promise<Array<number>> {\n    return this.getBuilderContext(\"poolConfig\", trx).insert([\n      {\n        poolConfigId: poolConfig.id,\n        token0: poolConfig.token0,\n        token1: poolConfig.token1,\n        fee: poolConfig.fee,\n        tickSpacing: poolConfig.tickSpacing,\n        timestamp: DateConverter.formatDate(new Date(), DATE_FORMAT),\n      },\n    ]);\n  }\n\n  private deserializeSnapshot(\n    snapshot: SnapshotRecord,\n    poolConfig: PoolConfig\n  ) {\n    return {\n      id: snapshot.snapshotId,\n      description: snapshot.description,\n      poolConfig,\n      token0Balance: JSBIDeserializer(snapshot.token0Balance),\n      token1Balance: JSBIDeserializer(snapshot.token1Balance),\n      sqrtPriceX96: JSBIDeserializer(snapshot.sqrtPriceX96),\n      liquidity: JSBIDeserializer(snapshot.liquidity),\n      tickCurrent: snapshot.tickCurrent,\n      feeGrowthGlobal0X128: JSBIDeserializer(snapshot.feeGrowthGlobal0X128),\n      feeGrowthGlobal1X128: JSBIDeserializer(snapshot.feeGrowthGlobal1X128),\n      tickManager: Serializer.deserialize(TickManager, snapshot.tickManager),\n      positionManager: Serializer.deserialize(\n        PositionManager,\n        snapshot.positionManager\n      ),\n      timestamp: DateConverter.parseDate(snapshot.timestamp),\n    };\n  }\n\n  private getBuilderContext(\n    tableName: string,\n    trx?: Knex.Transaction\n  ): Knex.QueryBuilder {\n    return trx ? trx(tableName) : this.knex(tableName);\n  }\n}\n"
  },
  {
    "path": "src/manager/SimulatorConsoleVisitor.ts",
    "content": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { SimulatorVisitor } from \"../interface/SimulatorVisitor\";\nimport { PoolState } from \"../model/PoolState\";\nimport { Transition } from \"../model/Transition\";\nimport { toString as printPoolConfig } from \"../model/PoolConfig\";\n\nimport * as log4js from \"log4js\";\n\nconst logger = log4js.getLogger(\"SimulatorConsoleVisitor\");\n\nexport class SimulatorConsoleVisitor implements SimulatorVisitor {\n  visitTransition(\n    transition: Transition,\n    callback?: (transition: Transition, returnValue: string) => void\n  ): Promise<string> {\n    logger.debug(transition.toString());\n    if (callback) callback(transition, transition.toString());\n    return Promise.resolve(\"ok\");\n  }\n\n  visitPoolState(\n    poolState: PoolState,\n    callback?: (poolState: PoolState, returnValue: string) => void\n  ): Promise<string> {\n    let corePool = poolState.recoverCorePool(true);\n    logger.debug(corePool.toString());\n    if (callback) callback(poolState, corePool.toString());\n    return Promise.resolve(\"ok\");\n  }\n\n  visitConfigurableCorePool(\n    configurableCorePool: ConfigurableCorePool,\n    callback?: (\n      configurableCorePool: ConfigurableCorePool,\n      returnValue: string\n    ) => void\n  ): Promise<string> {\n    let poolConfig = configurableCorePool.poolState.poolConfig;\n    logger.debug(printPoolConfig(poolConfig));\n    if (callback) callback(configurableCorePool, printPoolConfig(poolConfig));\n    return Promise.resolve(\"ok\");\n  }\n}\n"
  },
  {
    "path": "src/manager/SimulatorPersistenceVisitor.ts",
    "content": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { SimulatorVisitor } from \"../interface/SimulatorVisitor\";\nimport { PoolState } from \"../model/PoolState\";\nimport { Transition } from \"../model/Transition\";\nimport { SimulationDataManager } from \"../interface/SimulationDataManager\";\n\nexport class SimulatorPersistenceVisitor implements SimulatorVisitor {\n  private simulationDataManager: SimulationDataManager;\n\n  constructor(dbManager: SimulationDataManager) {\n    this.simulationDataManager = dbManager;\n  }\n\n  visitTransition(transition: Transition): Promise<string> {\n    return Promise.resolve(\"not implemented.\");\n  }\n\n  visitPoolState(\n    poolState: PoolState,\n    callback?: (poolState: PoolState, returnValue: number) => void\n  ): Promise<string> {\n    poolState.recoverCorePool(true);\n    return this.simulationDataManager\n      .persistSnapshot(poolState)\n      .then((returnValue) => {\n        if (callback) callback(poolState, returnValue);\n        return Promise.resolve(\"ok\");\n      });\n  }\n\n  visitConfigurableCorePool(\n    configurableCorePool: ConfigurableCorePool\n  ): Promise<string> {\n    return Promise.resolve(\"not implemented.\");\n  }\n}\n"
  },
  {
    "path": "src/manager/SimulatorRoadmapManager.ts",
    "content": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { Roadmap, toString as printRoadmap } from \"../model/Roadmap\";\nimport { toString as printPoolConfig } from \"../model/PoolConfig\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { PoolStateContainer } from \"../interface/PoolStateContainer\";\nimport { SimulatorRoadmapManager as ISimulatorRoadmapManager } from \"../interface/SimulatorRoadmapManager\";\nimport { PoolState } from \"../model/PoolState\";\nimport { PoolStateHelper } from \"../util/PoolStateHelper\";\nimport { SimulationDataManager } from \"../interface/SimulationDataManager\";\n\nimport * as log4js from \"log4js\";\n\nconst logger = log4js.getLogger(\"SimulatorRoadmapManager\");\n\nexport class SimulatorRoadmapManager\n  implements ISimulatorRoadmapManager, PoolStateContainer\n{\n  private simulationDataManager: SimulationDataManager;\n  private poolStates: Map<string, PoolState>;\n  private configurableCorePools: Map<string, ConfigurableCorePool> = new Map();\n\n  constructor(dbManager: SimulationDataManager) {\n    this.poolStates = new Map();\n    this.simulationDataManager = dbManager;\n  }\n\n  addPoolState(poolState: PoolState): string {\n    this.poolStates.set(poolState.id, poolState);\n    return poolState.id;\n  }\n\n  getPoolState(poolStateId: string): PoolState | undefined {\n    return this.poolStates.get(poolStateId);\n  }\n\n  hasPoolState(poolStateId: string): boolean {\n    return this.poolStates.has(poolStateId);\n  }\n\n  addRoute(configurableCorePool: ConfigurableCorePool) {\n    this.configurableCorePools.set(\n      configurableCorePool.id,\n      configurableCorePool\n    );\n  }\n\n  printRoute(configurableCorePoolId: string): Promise<void> {\n    if (!this.configurableCorePools.has(configurableCorePoolId)) {\n      throw new Error(\"Can't find CorePool, id: \" + configurableCorePoolId);\n    }\n    let pool = this.configurableCorePools.get(configurableCorePoolId)!;\n    return pool.showStateTransitionRoute(pool.poolState.id);\n  }\n\n  listRoutes(): Array<ConfigurableCorePool> {\n    return [...this.configurableCorePools.values()];\n  }\n\n  persistRoute(\n    configurableCorePoolId: string,\n    description: string\n  ): Promise<string> {\n    if (!this.configurableCorePools.has(configurableCorePoolId)) {\n      throw new Error(\"Can't find CorePool, id: \" + configurableCorePoolId);\n    }\n    let pool = this.configurableCorePools.get(configurableCorePoolId)!;\n    let roadmapId: string;\n    return pool\n      .persistSnapshots(pool.poolState.id)\n      .then((snapshotIds) => {\n        let roadmap = new Roadmap(description, snapshotIds);\n        roadmapId = roadmap.id;\n        return this.simulationDataManager.persistRoadmap(roadmap);\n      })\n      .then(() => Promise.resolve(roadmapId));\n  }\n\n  loadAndPrintRoute(roadmapId: string): Promise<void> {\n    return this.simulationDataManager\n      .getRoadmap(roadmapId)\n      .then((roadmap: Roadmap | undefined) => {\n        if (!roadmap)\n          return Promise.reject(\n            new Error(\"Can't find Roadmap, id: \" + roadmapId)\n          );\n        logger.debug(printRoadmap(roadmap));\n        return this.simulationDataManager.getSnapshots(roadmap.snapshots);\n      })\n      .then((snapshots: Snapshot[]) => {\n        if (snapshots.length == 0) return Promise.resolve();\n        logger.debug(printPoolConfig(snapshots[0].poolConfig));\n        snapshots.forEach((snapshot: Snapshot) => {\n          let recoveredCorePool =\n            PoolStateHelper.buildCorePoolBySnapshot(snapshot);\n          logger.debug(recoveredCorePool.toString());\n        });\n        return Promise.resolve();\n      });\n  }\n}\n"
  },
  {
    "path": "src/manager/TickManager.ts",
    "content": "import JSBI from \"jsbi\";\nimport assert from \"assert\";\nimport { Tick } from \"../model/Tick\";\nimport { jsonMapMember, jsonObject } from \"typedjson\";\nimport { FullMath } from \"../util/FullMath\";\nimport { TickView } from \"../interface/TickView\";\n\n@jsonObject\nexport class TickManager {\n  @jsonMapMember(Number, Tick, { name: \"ticks_json\" })\n  private _sortedTicks: Map<number, Tick>;\n\n  public get sortedTicks(): Map<number, Tick> {\n    return this._sortedTicks;\n  }\n\n  constructor(ticks: Map<number, Tick> = new Map()) {\n    this._sortedTicks = ticks;\n    this.sortTicks();\n  }\n\n  getTickAndInitIfAbsent(tickIndex: number): Tick {\n    if (this.sortedTicks.has(tickIndex))\n      return this.sortedTicks.get(tickIndex)!;\n    const newTick = new Tick(tickIndex);\n    this.set(newTick);\n    this.sortTicks();\n    return newTick;\n  }\n\n  getTickReadonly(tickIndex: number): TickView {\n    if (this.sortedTicks.has(tickIndex))\n      return this.sortedTicks.get(tickIndex)!;\n    return new Tick(tickIndex);\n  }\n\n  set(tick: Tick) {\n    this.sortedTicks.set(tick.tickIndex, tick);\n    this.sortTicks();\n  }\n\n  private nextInitializedTick(\n    sortedTicks: readonly Tick[],\n    tick: number,\n    lte: boolean\n  ): Tick {\n    if (lte) {\n      assert(!this.isBelowSmallest(sortedTicks, tick), \"BELOW_SMALLEST\");\n      if (this.isAtOrAboveLargest(sortedTicks, tick)) {\n        return sortedTicks[sortedTicks.length - 1];\n      }\n      const index = this.binarySearch(sortedTicks, tick);\n      return sortedTicks[index];\n    } else {\n      assert(\n        !this.isAtOrAboveLargest(sortedTicks, tick),\n        \"AT_OR_ABOVE_LARGEST\"\n      );\n      if (this.isBelowSmallest(sortedTicks, tick)) {\n        return sortedTicks[0];\n      }\n      const index = this.binarySearch(sortedTicks, tick);\n      return sortedTicks[index + 1];\n    }\n  }\n\n  getNextInitializedTick(\n    tick: number,\n    tickSpacing: number,\n    lte: boolean\n  ): { nextTick: number; initialized: boolean } {\n    const sortedTicks = this.getSortedTicks();\n    let compressed = Math.floor(tick / tickSpacing); // matches rounding in the code\n    // if (tick < 0 && tick % tickSpacing != 0) compressed--;\n    if (lte) {\n      const wordPos = compressed >> 8;\n      const minimum = (wordPos << 8) * tickSpacing;\n\n      if (this.isBelowSmallest(sortedTicks, tick)) {\n        return { nextTick: minimum, initialized: false };\n      }\n\n      const index = this.nextInitializedTick(sortedTicks, tick, lte).tickIndex;\n      const nextInitializedTick = Math.max(minimum, index);\n      return {\n        nextTick: nextInitializedTick,\n        initialized: nextInitializedTick === index,\n      };\n    } else {\n      const wordPos = (compressed + 1) >> 8;\n      // const maximum = ((wordPos + 1) << 8) * tickSpacing - 1;\n      const maximum = (((wordPos + 1) << 8) - 1) * tickSpacing;\n\n      if (this.isAtOrAboveLargest(sortedTicks, tick)) {\n        return { nextTick: maximum, initialized: false };\n      }\n\n      const index = this.nextInitializedTick(sortedTicks, tick, lte).tickIndex;\n      const nextInitializedTick = Math.min(maximum, index);\n      return {\n        nextTick: nextInitializedTick,\n        initialized: nextInitializedTick === index,\n      };\n    }\n  }\n\n  getFeeGrowthInside(\n    tickLower: number,\n    tickUpper: number,\n    tickCurrent: number,\n    feeGrowthGlobal0X128: JSBI,\n    feeGrowthGlobal1X128: JSBI\n  ): { feeGrowthInside0X128: JSBI; feeGrowthInside1X128: JSBI } {\n    // assert(\n    //   this.sortedTicks.has(tickLower) && this.sortedTicks.has(tickUpper),\n    //   \"INVALID_TICK\"\n    // );\n    const lower = this.getTickAndInitIfAbsent(tickLower);\n    const upper = this.getTickAndInitIfAbsent(tickUpper);\n    let feeGrowthBelow0X128: JSBI;\n    let feeGrowthBelow1X128: JSBI;\n    if (tickCurrent >= tickLower) {\n      feeGrowthBelow0X128 = lower.feeGrowthOutside0X128;\n      feeGrowthBelow1X128 = lower.feeGrowthOutside1X128;\n    } else {\n      feeGrowthBelow0X128 = JSBI.subtract(\n        feeGrowthGlobal0X128,\n        lower.feeGrowthOutside0X128\n      );\n      feeGrowthBelow1X128 = JSBI.subtract(\n        feeGrowthGlobal1X128,\n        lower.feeGrowthOutside1X128\n      );\n    }\n\n    let feeGrowthAbove0X128: JSBI;\n    let feeGrowthAbove1X128: JSBI;\n    if (tickCurrent < tickUpper) {\n      feeGrowthAbove0X128 = upper.feeGrowthOutside0X128;\n      feeGrowthAbove1X128 = upper.feeGrowthOutside1X128;\n    } else {\n      feeGrowthAbove0X128 = JSBI.subtract(\n        feeGrowthGlobal0X128,\n        upper.feeGrowthOutside0X128\n      );\n      feeGrowthAbove1X128 = JSBI.subtract(\n        feeGrowthGlobal1X128,\n        upper.feeGrowthOutside1X128\n      );\n    }\n    return {\n      feeGrowthInside0X128: JSBI.subtract(\n        FullMath.mod256Sub(feeGrowthGlobal0X128, feeGrowthBelow0X128),\n        feeGrowthAbove0X128\n      ),\n      feeGrowthInside1X128: JSBI.subtract(\n        FullMath.mod256Sub(feeGrowthGlobal1X128, feeGrowthBelow1X128),\n        feeGrowthAbove1X128\n      ),\n    };\n  }\n\n  clear(tick: number) {\n    this.sortedTicks.delete(tick);\n    this.sortTicks();\n  }\n\n  private sortTicks() {\n    const sortedTicks = new Map(\n      [...this.sortedTicks.entries()].sort((a, b) => a[0] - b[0])\n    );\n    this._sortedTicks = sortedTicks;\n  }\n\n  private getSortedTicks(): Tick[] {\n    return Array.from(this.sortedTicks.values());\n  }\n\n  private isBelowSmallest(sortedTicks: readonly Tick[], tick: number): boolean {\n    if (sortedTicks.length === 0) return true;\n    return tick < sortedTicks[0].tickIndex;\n  }\n\n  private isAtOrAboveLargest(\n    sortedTicks: readonly Tick[],\n    tick: number\n  ): boolean {\n    if (sortedTicks.length === 0) return true;\n    return tick >= sortedTicks[sortedTicks.length - 1].tickIndex;\n  }\n\n  private binarySearch(sortedTicks: readonly Tick[], tick: number): number {\n    assert(!this.isBelowSmallest(sortedTicks, tick), \"BELOW_SMALLEST\");\n\n    let l = 0;\n    let r = sortedTicks.length - 1;\n    let i;\n    while (true) {\n      i = Math.floor((l + r) / 2);\n\n      if (\n        sortedTicks[i].tickIndex <= tick &&\n        (i === sortedTicks.length - 1 || sortedTicks[i + 1].tickIndex > tick)\n      ) {\n        return i;\n      }\n\n      if (sortedTicks[i].tickIndex < tick) {\n        l = i + 1;\n      } else {\n        r = i - 1;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/manager/index.ts",
    "content": "export * from \"./SQLiteSimulationDataManager\";\nexport * from \"./EventDBManager\";\n"
  },
  {
    "path": "src/model/PoolConfig.ts",
    "content": "import { FeeAmount } from \"../enum/FeeAmount\";\nimport { IdGenerator } from \"../util/IdGenerator\";\n\nexport class PoolConfig {\n  readonly id: string;\n  readonly tickSpacing: number;\n  readonly token0: string;\n  readonly token1: string;\n  readonly fee: FeeAmount;\n\n  constructor(\n    tickSpacing: number,\n    token0: string,\n    token1: string,\n    fee: FeeAmount\n  ) {\n    this.id = IdGenerator.guid();\n    this.tickSpacing = tickSpacing;\n    this.token0 = token0;\n    this.token1 = token1;\n    this.fee = fee;\n  }\n}\n\nexport function toString(poolConfig: PoolConfig): string {\n  return `\n    Pool Config:\n        id: ${poolConfig.id}\n        tickSpacing: ${poolConfig.tickSpacing}\n        token0: ${poolConfig.token0}\n        token1: ${poolConfig.token1}\n        fee: ${poolConfig.fee}\n  `;\n}\n"
  },
  {
    "path": "src/model/PoolState.ts",
    "content": "import JSBI from \"jsbi\";\nimport { PoolConfig } from \"./PoolConfig\";\nimport { Transition } from \"../model/Transition\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { PositionManager } from \"../manager/PositionManager\";\nimport { TickManager } from \"../manager/TickManager\";\nimport { IdGenerator } from \"../util/IdGenerator\";\nimport { Record } from \"../entity/Record\";\nimport { Visitable } from \"../interface/Visitable\";\nimport { SimulatorVisitor } from \"../interface/SimulatorVisitor\";\nimport { PoolStateHelper } from \"../util/PoolStateHelper\";\nimport { CorePool } from \"../core/CorePool\";\nimport { ActionType } from \"../enum/ActionType\";\nimport { Serializer } from \"../util/Serializer\";\nimport { Transition as TransitionView } from \"../interface/Transition\";\n\nconst DEFAULT_SNAPSHOT_DESCRIPTION = \"Automated for caching\";\nexport class PoolState implements Visitable {\n  readonly id: string;\n  readonly baseSnapshot: Snapshot | undefined;\n  private _snapshot: Snapshot | undefined;\n  readonly poolConfig: PoolConfig;\n  readonly transitionSource: Transition | undefined;\n  readonly transitionTargets: Transition[] = new Array<Transition>();\n  readonly timestamp: Date = new Date();\n\n  constructor(\n    poolConfig?: PoolConfig,\n    baseSnapshot?: Snapshot,\n    fromTransition?: Transition\n  ) {\n    if (!poolConfig && !baseSnapshot) {\n      throw new Error(\"Please provide a pool config or a base snapshot!\");\n    }\n    this.poolConfig = baseSnapshot ? baseSnapshot.poolConfig : poolConfig!;\n    this.id = baseSnapshot ? baseSnapshot.id : IdGenerator.guid();\n    this.baseSnapshot = baseSnapshot;\n    this.transitionSource = fromTransition;\n  }\n\n  public get snapshot(): Snapshot | undefined {\n    return this._snapshot;\n  }\n\n  static from(baseSnapshot: Snapshot): PoolState {\n    return new PoolState(undefined, baseSnapshot);\n  }\n\n  takeSnapshot(\n    description: string,\n    token0Balance: JSBI,\n    token1Balance: JSBI,\n    sqrtPriceX96: JSBI,\n    liquidity: JSBI,\n    tickCurrent: number,\n    feeGrowthGlobal0X128: JSBI,\n    feeGrowthGlobal1X128: JSBI,\n    tickManager: TickManager,\n    positionManager: PositionManager\n  ) {\n    this._snapshot = {\n      id: this.id,\n      description,\n      poolConfig: this.poolConfig,\n      token0Balance,\n      token1Balance,\n      sqrtPriceX96,\n      liquidity,\n      tickCurrent,\n      feeGrowthGlobal0X128,\n      feeGrowthGlobal1X128,\n      tickManager: Serializer.deserialize(\n        TickManager,\n        Serializer.serialize(TickManager, tickManager)\n      ),\n      positionManager: Serializer.deserialize(\n        PositionManager,\n        Serializer.serialize(PositionManager, positionManager)\n      ),\n      timestamp: new Date(),\n    };\n  }\n\n  accept(\n    visitor: SimulatorVisitor,\n    callback?: (poolState: PoolState, returnValue: any) => void\n  ): Promise<string> {\n    return visitor.visitPoolState(this, callback);\n  }\n\n  recoverCorePool(takeSnapshot?: boolean): CorePool {\n    let corePool = this.hasSnapshot()\n      ? PoolStateHelper.buildCorePoolBySnapshot(this.snapshot as Snapshot)\n      : PoolStateHelper.recoverCorePoolByPoolStateChain(this);\n    if (takeSnapshot && !this.hasSnapshot()) {\n      this.takeSnapshot(\n        DEFAULT_SNAPSHOT_DESCRIPTION,\n        corePool.token0Balance,\n        corePool.token1Balance,\n        corePool.sqrtPriceX96,\n        corePool.liquidity,\n        corePool.tickCurrent,\n        corePool.feeGrowthGlobal0X128,\n        corePool.feeGrowthGlobal1X128,\n        corePool.tickManager,\n        corePool.positionManager\n      );\n    }\n    return corePool;\n  }\n\n  clearSnapshot(cachingOnly: boolean = false) {\n    if (!this.hasSnapshot()) return;\n    if (\n      !cachingOnly ||\n      this.snapshot!.description === DEFAULT_SNAPSHOT_DESCRIPTION\n    )\n      this._snapshot = undefined;\n  }\n\n  hasSnapshot(): boolean {\n    return this.snapshot !== undefined;\n  }\n\n  hasBaseSnapshot(): boolean {\n    return this.baseSnapshot !== undefined;\n  }\n\n  addTransitionTarget(record: Record): Transition {\n    const transition = new Transition(this, record);\n    this.transitionTargets.push(transition);\n    return transition;\n  }\n\n  getTransitionSource(): TransitionView | undefined {\n    return this.transitionSource;\n  }\n\n  getTransitionTargets(): TransitionView[] {\n    return this.transitionTargets;\n  }\n\n  fork(): PoolState {\n    let record: Record = {\n      id: IdGenerator.guid(),\n      actionType: ActionType.FORK,\n      actionParams: { type: ActionType.FORK },\n      actionReturnValues: {},\n      timestamp: new Date(),\n    };\n    let transition: Transition = this.addTransitionTarget(record);\n    let forkedPoolState = new PoolState(\n      this.poolConfig,\n      this.baseSnapshot,\n      transition\n    );\n    transition.target = forkedPoolState;\n    forkedPoolState._snapshot = this.snapshot;\n    return forkedPoolState;\n  }\n}\n"
  },
  {
    "path": "src/model/Position.ts",
    "content": "import JSBI from \"jsbi\";\nimport { jsonMember, jsonObject } from \"typedjson\";\nimport { Q128, ZERO } from \"../enum/InternalConstants\";\nimport { FullMath } from \"../util/FullMath\";\nimport { LiquidityMath } from \"../util/LiquidityMath\";\nimport { JSBIDeserializer, JSBISerializer } from \"../util/Serializer\";\nimport assert from \"assert\";\n\n@jsonObject\nexport class Position {\n  @jsonMember({ deserializer: JSBIDeserializer, serializer: JSBISerializer })\n  private _liquidity: JSBI = JSBI.BigInt(0);\n  @jsonMember({ deserializer: JSBIDeserializer, serializer: JSBISerializer })\n  private _feeGrowthInside0LastX128: JSBI = JSBI.BigInt(0);\n  @jsonMember({ deserializer: JSBIDeserializer, serializer: JSBISerializer })\n  private _feeGrowthInside1LastX128: JSBI = JSBI.BigInt(0);\n  @jsonMember({ deserializer: JSBIDeserializer, serializer: JSBISerializer })\n  private _tokensOwed0: JSBI = JSBI.BigInt(0);\n  @jsonMember({ deserializer: JSBIDeserializer, serializer: JSBISerializer })\n  private _tokensOwed1: JSBI = JSBI.BigInt(0);\n\n  public get liquidity(): JSBI {\n    return this._liquidity;\n  }\n\n  public get feeGrowthInside0LastX128(): JSBI {\n    return this._feeGrowthInside0LastX128;\n  }\n\n  public get feeGrowthInside1LastX128(): JSBI {\n    return this._feeGrowthInside1LastX128;\n  }\n\n  public get tokensOwed0(): JSBI {\n    return this._tokensOwed0;\n  }\n\n  public get tokensOwed1(): JSBI {\n    return this._tokensOwed1;\n  }\n\n  update(\n    liquidityDelta: JSBI,\n    feeGrowthInside0X128: JSBI,\n    feeGrowthInside1X128: JSBI\n  ) {\n    let liquidityNext: JSBI;\n    if (JSBI.equal(liquidityDelta, ZERO)) {\n      // assert(JSBI.greaterThan(this.liquidity, ZERO), \"NP\");\n      liquidityNext = this.liquidity;\n    } else {\n      liquidityNext = LiquidityMath.addDelta(this.liquidity, liquidityDelta);\n    }\n\n    const tokensOwed0 = FullMath.mulDiv(\n      JSBI.subtract(feeGrowthInside0X128, this.feeGrowthInside0LastX128),\n      this.liquidity,\n      Q128\n    );\n    const tokensOwed1 = FullMath.mulDiv(\n      JSBI.subtract(feeGrowthInside1X128, this.feeGrowthInside1LastX128),\n      this.liquidity,\n      Q128\n    );\n\n    if (JSBI.notEqual(liquidityDelta, ZERO)) this._liquidity = liquidityNext;\n    this._feeGrowthInside0LastX128 = feeGrowthInside0X128;\n    this._feeGrowthInside1LastX128 = feeGrowthInside1X128;\n    if (\n      JSBI.greaterThan(tokensOwed0, ZERO) ||\n      JSBI.greaterThan(tokensOwed1, ZERO)\n    ) {\n      this._tokensOwed0 = JSBI.add(this.tokensOwed0, tokensOwed0);\n      this._tokensOwed1 = JSBI.add(this.tokensOwed1, tokensOwed1);\n    }\n  }\n\n  updateBurn(newTokensOwed0: JSBI, newTokensOwed1: JSBI) {\n    this._tokensOwed0 = newTokensOwed0;\n    this._tokensOwed1 = newTokensOwed1;\n  }\n\n  isEmpty(): boolean {\n    return (\n      JSBI.equal(this._liquidity, ZERO) &&\n      JSBI.equal(this._tokensOwed0, ZERO) &&\n      JSBI.equal(this._tokensOwed1, ZERO)\n    );\n  }\n}\n"
  },
  {
    "path": "src/model/Roadmap.ts",
    "content": "import { IdGenerator } from \"../util/IdGenerator\";\n\nexport class Roadmap {\n  readonly id: string;\n  readonly description: string;\n  readonly snapshots: number[];\n  readonly timestamp: Date;\n\n  constructor(description: string, snapshots: number[]) {\n    this.id = IdGenerator.guid();\n    this.description = description;\n    this.snapshots = snapshots;\n    this.timestamp = new Date();\n  }\n}\n\nexport function toString(roadmap: Roadmap): string {\n  return `\n    Roadmap:\n        id: ${roadmap.id}\n        description: ${roadmap.description}\n        snapshotLength: ${roadmap.snapshots.length}\n        timestamp: ${roadmap.timestamp}\n  `;\n}\n"
  },
  {
    "path": "src/model/Tick.ts",
    "content": "import JSBI from \"jsbi\";\nimport assert from \"assert\";\nimport { TickMath } from \"../util/TickMath\";\nimport { jsonMember, jsonObject } from \"typedjson\";\nimport { JSBIDeserializer, JSBISerializer } from \"../util/Serializer\";\nimport { LiquidityMath } from \"../util/LiquidityMath\";\nimport { MaxInt128, MinInt128, ZERO } from \"../enum/InternalConstants\";\nimport { TickView } from \"../interface/TickView\";\nimport { toJSBI } from \"../util\";\n\n@jsonObject({\n  initializer: (_, rawSourceObject: TickView) => {\n    return new Tick(rawSourceObject.tickIndex);\n  },\n})\nexport class Tick {\n  @jsonMember(Number, { name: \"tickIndex\" })\n  protected _tickIndex: number = 0;\n  @jsonMember({\n    name: \"liquidityGross\",\n    deserializer: JSBIDeserializer,\n    serializer: JSBISerializer,\n  })\n  protected _liquidityGross: JSBI = ZERO;\n  @jsonMember({\n    name: \"liquidityNet\",\n    deserializer: JSBIDeserializer,\n    serializer: JSBISerializer,\n  })\n  protected _liquidityNet: JSBI = ZERO;\n  @jsonMember({\n    name: \"feeGrowthOutside0X128\",\n    deserializer: JSBIDeserializer,\n    serializer: JSBISerializer,\n  })\n  protected _feeGrowthOutside0X128: JSBI = ZERO;\n  @jsonMember({\n    name: \"feeGrowthOutside1X128\",\n    deserializer: JSBIDeserializer,\n    serializer: JSBISerializer,\n  })\n  protected _feeGrowthOutside1X128: JSBI = ZERO;\n\n  constructor(\n    tickIndex: number,\n    liquidityGross?: JSBI,\n    liquidityNet?: JSBI,\n    feeGrowthOutside0X128?: JSBI,\n    feeGrowthOutside1X128?: JSBI\n  ) {\n    assert(\n      tickIndex >= TickMath.MIN_TICK && tickIndex <= TickMath.MAX_TICK,\n      \"TICK\"\n    );\n    this._tickIndex = tickIndex;\n    this._liquidityGross = liquidityGross ? liquidityGross : ZERO;\n    this._liquidityNet = liquidityNet ? liquidityNet : ZERO;\n    this._feeGrowthOutside0X128 = feeGrowthOutside0X128\n      ? feeGrowthOutside0X128\n      : ZERO;\n    this._feeGrowthOutside1X128 = feeGrowthOutside1X128\n      ? feeGrowthOutside1X128\n      : ZERO;\n  }\n\n  static fromOnchainData(\n    tickIndex: number,\n    liquidityGross: bigint,\n    liquidityNet: bigint,\n    feeGrowthOutside0X128: bigint,\n    feeGrowthOutside1X128: bigint\n  ): Tick {\n    return new Tick(\n      tickIndex,\n      toJSBI(liquidityGross),\n      toJSBI(liquidityNet),\n      toJSBI(feeGrowthOutside0X128),\n      toJSBI(feeGrowthOutside1X128)\n    );\n  }\n\n  public get tickIndex(): number {\n    return this._tickIndex;\n  }\n\n  public get liquidityGross(): JSBI {\n    return this._liquidityGross;\n  }\n\n  public get liquidityNet(): JSBI {\n    return this._liquidityNet;\n  }\n\n  public get feeGrowthOutside0X128(): JSBI {\n    return this._feeGrowthOutside0X128;\n  }\n\n  public get feeGrowthOutside1X128(): JSBI {\n    return this._feeGrowthOutside1X128;\n  }\n\n  public get initialized(): boolean {\n    return JSBI.notEqual(this.liquidityGross, ZERO);\n  }\n\n  update(\n    liquidityDelta: JSBI,\n    tickCurrent: number,\n    feeGrowthGlobal0X128: JSBI,\n    feeGrowthGlobal1X128: JSBI,\n    upper: boolean,\n    maxLiquidity: JSBI\n  ): boolean {\n    const liquidityGrossBefore = this.liquidityGross;\n    const liquidityGrossAfter = LiquidityMath.addDelta(\n      liquidityGrossBefore,\n      liquidityDelta\n    );\n    assert(JSBI.lessThanOrEqual(liquidityGrossAfter, maxLiquidity), \"LO\");\n    const flipped =\n      JSBI.equal(liquidityGrossAfter, ZERO) !=\n      JSBI.equal(liquidityGrossBefore, ZERO);\n    if (JSBI.equal(liquidityGrossBefore, ZERO)) {\n      if (this.tickIndex <= tickCurrent) {\n        this._feeGrowthOutside0X128 = feeGrowthGlobal0X128;\n        this._feeGrowthOutside1X128 = feeGrowthGlobal1X128;\n      }\n    }\n    this._liquidityGross = liquidityGrossAfter;\n    this._liquidityNet = upper\n      ? JSBI.subtract(this._liquidityNet, liquidityDelta)\n      : JSBI.add(this._liquidityNet, liquidityDelta);\n    assert(JSBI.lessThanOrEqual(this.liquidityNet, MaxInt128));\n    assert(JSBI.greaterThanOrEqual(this.liquidityNet, MinInt128));\n    return flipped;\n  }\n\n  cross(feeGrowthGlobal0X128: JSBI, feeGrowthGlobal1X128: JSBI): JSBI {\n    this._feeGrowthOutside0X128 = JSBI.subtract(\n      feeGrowthGlobal0X128,\n      this._feeGrowthOutside0X128\n    );\n    this._feeGrowthOutside1X128 = JSBI.subtract(\n      feeGrowthGlobal1X128,\n      this._feeGrowthOutside1X128\n    );\n    return this._liquidityNet;\n  }\n}\n"
  },
  {
    "path": "src/model/Transition.ts",
    "content": "import { PoolState } from \"./PoolState\";\nimport { Record } from \"../entity/Record\";\nimport { Visitable } from \"../interface/Visitable\";\nimport { SimulatorVisitor } from \"../interface/SimulatorVisitor\";\nimport { Transition as TransitionView } from \"../interface/Transition\";\nimport { PoolStateView } from \"../interface/PoolStateView\";\nimport { printParams } from \"../util/Serializer\";\n\nexport class Transition implements Visitable, TransitionView {\n  private _source: PoolState;\n  private _target: PoolState | undefined;\n  private _record: Record;\n\n  public get source(): PoolState {\n    return this._source;\n  }\n\n  public get target(): PoolState | undefined {\n    return this._target;\n  }\n\n  public set target(value: PoolState | undefined) {\n    this._target = value;\n  }\n\n  public get record(): Record {\n    return this._record;\n  }\n\n  constructor(source: PoolState, record: Record) {\n    this._source = source;\n    this._record = record;\n  }\n\n  getSource(): PoolStateView {\n    return this.source;\n  }\n\n  getTarget(): PoolStateView {\n    return this.target!;\n  }\n\n  getRecord(): Record {\n    return this.record;\n  }\n\n  accept(visitor: SimulatorVisitor): Promise<string> {\n    return visitor.visitTransition(this);\n  }\n\n  toString(): string {\n    return `\n    Transition:\n        sourcePoolStateId: ${this.source.id}\n        targetPoolStateId: ${this.target!.id}\n    Record: \n        id: ${this.record.id}\n        actionType: ${this.record.actionType}\n        actionParams: ${printParams(this.record.actionParams)}\n        actionReturnValues: ${printParams(this.record.actionReturnValues)}\n        timestamp: ${this.record.timestamp}\n    `;\n  }\n}\n"
  },
  {
    "path": "src/model/index.ts",
    "content": "export { PoolConfig } from \"./PoolConfig\";\nexport { Roadmap } from \"./Roadmap\";\n"
  },
  {
    "path": "src/util/BNUtils.ts",
    "content": "import BigNumber from \"bignumber.js\";\nimport { BigNumber as BN } from \"ethers\";\nimport JSBI from \"jsbi\";\n\nBigNumber.config({ EXPONENTIAL_AT: 50 });\n\nexport function sum(bnArr: BN[]) {\n  return bnArr.reduce((prev, current) => {\n    return prev.add(current);\n  });\n}\n\nexport function mul10pow(bn: BN, n: number) {\n  return bn.mul(get10pow(n));\n}\n\nexport function div10pow(bn: BN, n: number) {\n  return bn.div(get10pow(n));\n}\n\nexport function get10pow(n: number) {\n  return BN.from(10).pow(n);\n}\n\nexport function isPositive(bn: BN): boolean {\n  return bn.gt(0);\n}\n\nexport function toBN(number: any): BN {\n  return BN.from(number.toString());\n}\n\nexport function toJSBI(number: any): JSBI {\n  return JSBI.BigInt(number.toString());\n}\n\nexport function convertTokenStrFromDecimal(\n  bnStr: string,\n  decimals: number\n): string {\n  return new BigNumber(bnStr).times(new BigNumber(10).pow(decimals)).toString();\n}\n\nexport function convertTokenStr(bnStr: string): string {\n  return new BigNumber(bnStr).toString();\n}\n"
  },
  {
    "path": "src/util/DateConverter.ts",
    "content": "import dayjs from \"dayjs\";\n\nexport abstract class DateConverter {\n  static parseDate(dateStr: string): Date {\n    return dayjs(dateStr).toDate();\n  }\n  static formatDate(date: Date, formatStr: string): string {\n    return dayjs(date).format(formatStr);\n  }\n}\n"
  },
  {
    "path": "src/util/DateUtils.ts",
    "content": "export function getDate(\n  year: number,\n  month: number,\n  day: number,\n  hour: number = 0,\n  minute: number = 0,\n  second: number = 0,\n  millisecond: number = 0\n): Date {\n  let date = new Date();\n  date.setFullYear(year, month - 1, day);\n  date.setHours(hour, minute, second, millisecond);\n  return date;\n}\n\nexport function getYesterday(date: Date): Date {\n  return new Date(date.getTime() - 24 * 60 * 60 * 1000);\n}\n\nexport function getTomorrow(date: Date): Date {\n  return new Date(date.getTime() + 24 * 60 * 60 * 1000);\n}\n\nexport function format(date: Date, fmt: string): string {\n  type template = {\n    \"M+\": number;\n    \"d+\": number;\n    \"H+\": number;\n    \"m+\": number;\n    \"s+\": number;\n    \"q+\": number;\n    S: number;\n  };\n  var o: template = {\n    \"M+\": date.getMonth() + 1,\n    \"d+\": date.getDate(),\n    \"H+\": date.getHours(),\n    \"m+\": date.getMinutes(),\n    \"s+\": date.getSeconds(),\n    \"q+\": Math.floor((date.getMonth() + 3) / 3),\n    S: date.getMilliseconds(),\n  };\n  if (/(y+)/.test(fmt))\n    fmt = fmt.replace(\n      RegExp.$1,\n      (date.getFullYear() + \"\").substr(4 - RegExp.$1.length)\n    );\n  let k: keyof template;\n  for (k in o)\n    if (new RegExp(\"(\" + k + \")\").test(fmt))\n      fmt = fmt.replace(\n        RegExp.$1,\n        RegExp.$1.length == 1\n          ? \"\" + o[k]\n          : (\"00\" + o[k]).substr((\"\" + o[k]).length)\n      );\n  return fmt;\n}\n"
  },
  {
    "path": "src/util/FileUtils.ts",
    "content": "import { accessSync, constants } from \"fs\";\nimport { basename } from \"path\";\n\nexport function exists(filePath: string): boolean {\n  try {\n    accessSync(filePath, constants.F_OK);\n    return true;\n  } catch (err) {\n    return false;\n  }\n}\n\nexport function getDatabaseNameFromPath(\n  filePath: string,\n  extToMove?: string\n): string {\n  return basename(filePath, extToMove);\n}\n"
  },
  {
    "path": "src/util/FullMath.ts",
    "content": "import JSBI from \"jsbi\";\nimport { ZERO, ONE, MaxUint256, TWO } from \"../enum/InternalConstants\";\nimport assert from \"assert\";\n\nexport abstract class FullMath {\n  static MAX_SAFE_INTEGER: JSBI = JSBI.BigInt(Number.MAX_SAFE_INTEGER);\n\n  static mulDiv(a: JSBI, b: JSBI, denominator: JSBI): JSBI {\n    const product = JSBI.multiply(a, b);\n    return JSBI.divide(product, denominator);\n  }\n\n  static mulDivRoundingUp(a: JSBI, b: JSBI, denominator: JSBI): JSBI {\n    const product = JSBI.multiply(a, b);\n    let result = JSBI.divide(product, denominator);\n    if (JSBI.greaterThan(JSBI.remainder(product, denominator), ZERO)) {\n      assert(JSBI.lessThan(result, MaxUint256), \"OVERFLOW\");\n      result = JSBI.add(result, ONE);\n    }\n    return result;\n  }\n\n  // simulates EVM uint256 \"a - b\" underflow behavior\n  static mod256Sub(a: JSBI, b: JSBI): JSBI {\n    assert(\n      JSBI.greaterThanOrEqual(a, ZERO) &&\n        JSBI.greaterThanOrEqual(b, ZERO) &&\n        JSBI.lessThanOrEqual(a, MaxUint256) &&\n        JSBI.lessThanOrEqual(b, MaxUint256)\n    );\n    return JSBI.remainder(\n      JSBI.subtract(JSBI.add(a, JSBI.exponentiate(TWO, JSBI.BigInt(256))), b),\n      JSBI.exponentiate(TWO, JSBI.BigInt(256))\n    );\n  }\n\n  static equalsWithTolerance(\n    a: JSBI,\n    b: JSBI,\n    toleranceInMinUnit: JSBI\n  ): boolean {\n    return (\n      JSBI.greaterThanOrEqual(\n        JSBI.subtract(a, b),\n        JSBI.BigInt(\n          JSBI.greaterThan(toleranceInMinUnit, ZERO)\n            ? JSBI.unaryMinus(toleranceInMinUnit)\n            : toleranceInMinUnit\n        )\n      ) &&\n      JSBI.lessThanOrEqual(\n        JSBI.subtract(a, b),\n        JSBI.BigInt(\n          JSBI.greaterThan(toleranceInMinUnit, ZERO)\n            ? toleranceInMinUnit\n            : JSBI.unaryMinus(toleranceInMinUnit)\n        )\n      )\n    );\n  }\n  /**\n   * Computes floor(sqrt(value))\n   * @param value the value for which to compute the square root, rounded down\n   */\n  static sqrt(value: JSBI): JSBI {\n    assert(JSBI.greaterThanOrEqual(value, ZERO), \"NEGATIVE\");\n\n    // rely on built in sqrt if possible\n    if (JSBI.lessThan(value, FullMath.MAX_SAFE_INTEGER)) {\n      return JSBI.BigInt(Math.floor(Math.sqrt(JSBI.toNumber(value))));\n    }\n\n    let z: JSBI;\n    let x: JSBI;\n    z = value;\n    x = JSBI.add(JSBI.divide(value, TWO), ONE);\n    while (JSBI.lessThan(x, z)) {\n      z = x;\n      x = JSBI.divide(JSBI.add(JSBI.divide(value, x), x), TWO);\n    }\n    return z;\n  }\n\n  static incrTowardInfinity(value: JSBI): JSBI {\n    assert(JSBI.notEqual(value, ZERO), \"ZERO\");\n    return JSBI.greaterThan(value, ZERO)\n      ? JSBI.add(value, ONE)\n      : JSBI.subtract(value, ONE);\n  }\n}\n"
  },
  {
    "path": "src/util/IdGenerator.ts",
    "content": "import { v4, validate } from \"uuid\";\n\nexport abstract class IdGenerator {\n  static guid = v4;\n  static validate = validate;\n}\n"
  },
  {
    "path": "src/util/LiquidityMath.ts",
    "content": "import JSBI from \"jsbi\";\nimport { NEGATIVE_ONE, ZERO, MaxUint128, Q96 } from \"../enum/InternalConstants\";\nimport assert from \"assert\";\nimport { SqrtPriceMath } from \"./SqrtPriceMath\";\n\nexport abstract class LiquidityMath {\n  static addDelta(x: JSBI, y: JSBI): JSBI {\n    assert(JSBI.lessThanOrEqual(x, MaxUint128), \"OVERFLOW\");\n    assert(JSBI.lessThanOrEqual(y, MaxUint128), \"OVERFLOW\");\n    if (JSBI.lessThan(y, ZERO)) {\n      const negatedY = JSBI.multiply(y, NEGATIVE_ONE);\n      assert(JSBI.greaterThanOrEqual(x, negatedY), \"UNDERFLOW\");\n      return JSBI.subtract(x, negatedY);\n    } else {\n      assert(JSBI.lessThanOrEqual(JSBI.add(x, y), MaxUint128), \"OVERFLOW\");\n      return JSBI.add(x, y);\n    }\n  }\n\n  static getAmountsForLiquidity(\n    sqrtRatioX96: JSBI,\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    liquidity: JSBI\n  ): { amount0: JSBI; amount1: JSBI } {\n    let amount0: JSBI = ZERO;\n    let amount1: JSBI = ZERO;\n    if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {\n      [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];\n    }\n\n    if (JSBI.lessThanOrEqual(sqrtRatioX96, sqrtRatioAX96)) {\n      amount0 = SqrtPriceMath.getAmount0Delta(\n        sqrtRatioAX96,\n        sqrtRatioBX96,\n        liquidity\n      );\n    } else if (JSBI.lessThan(sqrtRatioX96, sqrtRatioBX96)) {\n      amount0 = SqrtPriceMath.getAmount0Delta(\n        sqrtRatioX96,\n        sqrtRatioBX96,\n        liquidity\n      );\n      amount1 = SqrtPriceMath.getAmount1Delta(\n        sqrtRatioAX96,\n        sqrtRatioX96,\n        liquidity\n      );\n    } else {\n      amount1 = SqrtPriceMath.getAmount1Delta(\n        sqrtRatioAX96,\n        sqrtRatioBX96,\n        liquidity\n      );\n    }\n    return { amount0, amount1 };\n  }\n\n  /**\n   * Computes the maximum amount of liquidity received for a given amount of token0, token1,\n   * and the prices at the tick boundaries.\n   * @param sqrtRatioCurrentX96 the current price\n   * @param sqrtRatioAX96 price at lower boundary\n   * @param sqrtRatioBX96 price at upper boundary\n   * @param amount0 token0 amount\n   * @param amount1 token1 amount\n   * @param useFullPrecision if false, liquidity will be maximized according to what the router can calculate,\n   * not what core can theoretically support\n   */\n  static maxLiquidityForAmounts(\n    sqrtRatioCurrentX96: JSBI,\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    amount0: JSBI,\n    amount1: JSBI,\n    useFullPrecision: boolean\n  ): JSBI {\n    if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {\n      [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];\n    }\n\n    const maxLiquidityForAmount0 = useFullPrecision\n      ? LiquidityMath.maxLiquidityForAmount0Precise\n      : LiquidityMath.maxLiquidityForAmount0Imprecise;\n\n    if (JSBI.lessThanOrEqual(sqrtRatioCurrentX96, sqrtRatioAX96)) {\n      return maxLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0);\n    } else if (JSBI.lessThan(sqrtRatioCurrentX96, sqrtRatioBX96)) {\n      const liquidity0 = maxLiquidityForAmount0(\n        sqrtRatioCurrentX96,\n        sqrtRatioBX96,\n        amount0\n      );\n      const liquidity1 = LiquidityMath.maxLiquidityForAmount1(\n        sqrtRatioAX96,\n        sqrtRatioCurrentX96,\n        amount1\n      );\n      return JSBI.lessThan(liquidity0, liquidity1) ? liquidity0 : liquidity1;\n    } else {\n      return LiquidityMath.maxLiquidityForAmount1(\n        sqrtRatioAX96,\n        sqrtRatioBX96,\n        amount1\n      );\n    }\n  }\n\n  private static maxLiquidityForAmount0Imprecise(\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    amount0: JSBI\n  ): JSBI {\n    if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {\n      [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];\n    }\n    const intermediate = JSBI.divide(\n      JSBI.multiply(sqrtRatioAX96, sqrtRatioBX96),\n      Q96\n    );\n    return JSBI.divide(\n      JSBI.multiply(amount0, intermediate),\n      JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96)\n    );\n  }\n\n  /**\n   * Returns a precise maximum amount of liquidity received for a given amount of token 0 by dividing by Q64 instead of Q96 in the intermediate step,\n   * and shifting the subtracted ratio left by 32 bits.\n   * @param sqrtRatioAX96 The price at the lower boundary\n   * @param sqrtRatioBX96 The price at the upper boundary\n   * @param amount0 The token0 amount\n   * @returns liquidity for amount0, precise\n   */\n  private static maxLiquidityForAmount0Precise(\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    amount0: JSBI\n  ): JSBI {\n    if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {\n      [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];\n    }\n\n    const numerator = JSBI.multiply(\n      JSBI.multiply(amount0, sqrtRatioAX96),\n      sqrtRatioBX96\n    );\n    const denominator = JSBI.multiply(\n      Q96,\n      JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96)\n    );\n\n    return JSBI.divide(numerator, denominator);\n  }\n\n  /**\n   * Computes the maximum amount of liquidity received for a given amount of token1\n   * @param sqrtRatioAX96 The price at the lower tick boundary\n   * @param sqrtRatioBX96 The price at the upper tick boundary\n   * @param amount1 The token1 amount\n   * @returns liquidity for amount1\n   */\n  private static maxLiquidityForAmount1(\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    amount1: JSBI\n  ): JSBI {\n    if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {\n      [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];\n    }\n    return JSBI.divide(\n      JSBI.multiply(amount1, Q96),\n      JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96)\n    );\n  }\n}\n"
  },
  {
    "path": "src/util/PoolStateHelper.ts",
    "content": "import { CorePool } from \"../core/CorePool\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { PoolState } from \"../model/PoolState\";\nimport { Record } from \"../entity/Record\";\nimport { PoolConfig } from \"../model/PoolConfig\";\nimport { ActionType } from \"../enum/ActionType\";\nimport { Serializer } from \"./Serializer\";\nimport { TickManager } from \"../manager/TickManager\";\nimport { PositionManager } from \"../manager/PositionManager\";\nimport { PoolStateView } from \"../interface/PoolStateView\";\nimport { Tick } from \"../model/Tick\";\nimport { toJSBI } from \"./BNUtils\";\nimport { ZERO } from \"../enum/InternalConstants\";\n\nexport abstract class PoolStateHelper {\n  static countHistoricalPoolStateTransitions(poolState: PoolStateView): number {\n    let fromTransition = poolState.getTransitionSource();\n    if (\n      !fromTransition ||\n      fromTransition.getRecord().actionType == ActionType.FORK\n    )\n      return 1;\n    return (\n      PoolStateHelper.countHistoricalPoolStateTransitions(\n        fromTransition.getSource()\n      ) + 1\n    );\n  }\n\n  static recoverCorePoolByPoolStateChain(poolState: PoolState): CorePool {\n    let fromTransition = poolState.transitionSource;\n    if (!fromTransition) {\n      return poolState.hasBaseSnapshot()\n        ? PoolStateHelper.buildCorePoolBySnapshot(poolState.baseSnapshot!)\n        : PoolStateHelper.buildCorePoolByPoolConfig(poolState.poolConfig);\n    } else {\n      if (poolState.hasSnapshot()) {\n        let snapshot = poolState.snapshot!;\n        return PoolStateHelper.buildCorePoolBySnapshot(snapshot);\n      } else {\n        return PoolStateHelper.applyRecordOnCorePool(\n          PoolStateHelper.recoverCorePoolByPoolStateChain(\n            fromTransition.source\n          ),\n          fromTransition.record\n        );\n      }\n    }\n  }\n\n  static buildCorePoolBySnapshot(snapshot: Snapshot): CorePool {\n    return new CorePool(\n      snapshot.poolConfig.token0,\n      snapshot.poolConfig.token1,\n      snapshot.poolConfig.fee,\n      snapshot.poolConfig.tickSpacing,\n      snapshot.token0Balance,\n      snapshot.token1Balance,\n      snapshot.sqrtPriceX96,\n      snapshot.liquidity,\n      snapshot.tickCurrent,\n      snapshot.feeGrowthGlobal0X128,\n      snapshot.feeGrowthGlobal1X128,\n      Serializer.deserialize(\n        TickManager,\n        Serializer.serialize(TickManager, snapshot.tickManager)\n      ),\n      Serializer.deserialize(\n        PositionManager,\n        Serializer.serialize(PositionManager, snapshot.positionManager)\n      )\n    );\n  }\n\n  static buildCorePoolByOnchainData(\n    poolConfig: PoolConfig,\n    sqrtPriceX96: bigint,\n    liquidity: bigint,\n    tickCurrent: number,\n    feeGrowthGlobal0X128: bigint,\n    feeGrowthGlobal1X128: bigint,\n    populatedTicks: Map<number, Tick>\n  ): CorePool {\n    return new CorePool(\n      poolConfig.token0,\n      poolConfig.token1,\n      poolConfig.fee,\n      poolConfig.tickSpacing,\n      ZERO,\n      ZERO,\n      toJSBI(sqrtPriceX96),\n      toJSBI(liquidity),\n      tickCurrent,\n      toJSBI(feeGrowthGlobal0X128),\n      toJSBI(feeGrowthGlobal1X128),\n      new TickManager(populatedTicks)\n    );\n  }\n\n  static buildCorePoolByPoolConfig(poolConfig: PoolConfig): CorePool {\n    return new CorePool(\n      poolConfig.token0,\n      poolConfig.token1,\n      poolConfig.fee,\n      poolConfig.tickSpacing\n    );\n  }\n\n  private static applyRecordOnCorePool(\n    corepool: CorePool,\n    record: Record\n  ): CorePool {\n    if (record.actionType == ActionType.FORK) {\n      return corepool;\n    }\n    let event: Function = corepool[record.actionType];\n    event.call(\n      corepool,\n      ...this.actionParamsToParamsArray(record.actionParams)\n    );\n    return corepool;\n  }\n\n  private static actionParamsToParamsArray(actionParams: any): Array<any> {\n    let paramKeys = Object.keys(actionParams);\n    paramKeys.splice(paramKeys.indexOf(\"type\"), 1);\n    return paramKeys.map((paramKey) => actionParams[paramKey]);\n  }\n}\n"
  },
  {
    "path": "src/util/Serializer.ts",
    "content": "import JSBI from \"jsbi\";\nimport { TypedJSON } from \"typedjson\";\nimport { Constructor } from \"typedjson/src/types\";\nimport { ZERO } from \"../enum/InternalConstants\";\n\nexport abstract class Serializer {\n  static serialize<T>(rootConstructor: Constructor<T>, object: T): string {\n    let serializer = new TypedJSON(rootConstructor);\n    return serializer.stringify(object);\n  }\n\n  static deserialize<T>(rootConstructor: Constructor<T>, jsonStr: string): T {\n    let serializer = new TypedJSON(rootConstructor);\n    return serializer.parse(jsonStr);\n  }\n}\n\nexport const JSBISerializer = (jsbi: JSBI): string => jsbi.toString();\nexport const JSBIDeserializer = (str: string): JSBI =>\n  str == undefined ? ZERO : JSBI.BigInt(str);\n\nexport const NumberArraySerializer = (arr: Array<number>): string =>\n  JSON.stringify(arr);\nexport const NumberArrayDeserializer = (str: string): Array<number> =>\n  JSON.parse(str);\n\nexport function printParams(params: object): string {\n  let str = \"{\";\n  for (let key in params) {\n    let value: any = params[key as keyof object];\n    if (value == null) {\n      str += key + \": null, \";\n    } else {\n      str += key + \": \" + (isObject(value) ? value.toString() : value) + \", \";\n    }\n  }\n  if (str.lastIndexOf(\" \") == str.length - 1) str = str.slice(0, -2);\n  str += \"}\";\n  return str;\n}\n\nfunction isObject(value: unknown): value is object {\n  return typeof value === \"object\";\n}\n"
  },
  {
    "path": "src/util/SqrtPriceMath.ts",
    "content": "import JSBI from \"jsbi\";\nimport { FullMath } from \"./FullMath\";\nimport {\n  ONE,\n  ZERO,\n  Q96,\n  MaxUint160,\n  MaxUint256,\n} from \"../enum/InternalConstants\";\nimport assert from \"assert\";\n\nfunction multiplyIn256(x: JSBI, y: JSBI): JSBI {\n  const product = JSBI.multiply(x, y);\n  return JSBI.bitwiseAnd(product, MaxUint256);\n}\n\nfunction addIn256(x: JSBI, y: JSBI): JSBI {\n  const sum = JSBI.add(x, y);\n  return JSBI.bitwiseAnd(sum, MaxUint256);\n}\n\nexport abstract class SqrtPriceMath {\n  static getAmount0Delta(\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    liquidity: JSBI\n  ): JSBI {\n    return JSBI.lessThan(liquidity, JSBI.BigInt(0))\n      ? JSBI.unaryMinus(\n          SqrtPriceMath.getAmount0DeltaWithRoundUp(\n            sqrtRatioAX96,\n            sqrtRatioBX96,\n            JSBI.unaryMinus(liquidity),\n            false\n          )\n        )\n      : SqrtPriceMath.getAmount0DeltaWithRoundUp(\n          sqrtRatioAX96,\n          sqrtRatioBX96,\n          liquidity,\n          true\n        );\n  }\n\n  static getAmount1Delta(\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    liquidity: JSBI\n  ): JSBI {\n    return JSBI.lessThan(liquidity, JSBI.BigInt(0))\n      ? JSBI.unaryMinus(\n          SqrtPriceMath.getAmount1DeltaWithRoundUp(\n            sqrtRatioAX96,\n            sqrtRatioBX96,\n            JSBI.unaryMinus(liquidity),\n            false\n          )\n        )\n      : SqrtPriceMath.getAmount1DeltaWithRoundUp(\n          sqrtRatioAX96,\n          sqrtRatioBX96,\n          liquidity,\n          true\n        );\n  }\n\n  static getNextSqrtPriceFromInput(\n    sqrtPX96: JSBI,\n    liquidity: JSBI,\n    amountIn: JSBI,\n    zeroForOne: boolean\n  ): JSBI {\n    assert(JSBI.greaterThan(sqrtPX96, ZERO));\n    assert(JSBI.greaterThan(liquidity, ZERO));\n\n    return zeroForOne\n      ? this.getNextSqrtPriceFromAmount0RoundingUp(\n          sqrtPX96,\n          liquidity,\n          amountIn,\n          true\n        )\n      : this.getNextSqrtPriceFromAmount1RoundingDown(\n          sqrtPX96,\n          liquidity,\n          amountIn,\n          true\n        );\n  }\n\n  static getNextSqrtPriceFromOutput(\n    sqrtPX96: JSBI,\n    liquidity: JSBI,\n    amountOut: JSBI,\n    zeroForOne: boolean\n  ): JSBI {\n    assert(JSBI.greaterThan(sqrtPX96, ZERO));\n    assert(JSBI.greaterThan(liquidity, ZERO));\n\n    return zeroForOne\n      ? this.getNextSqrtPriceFromAmount1RoundingDown(\n          sqrtPX96,\n          liquidity,\n          amountOut,\n          false\n        )\n      : this.getNextSqrtPriceFromAmount0RoundingUp(\n          sqrtPX96,\n          liquidity,\n          amountOut,\n          false\n        );\n  }\n\n  static getAmount0DeltaWithRoundUp(\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    liquidity: JSBI,\n    roundUp: boolean\n  ): JSBI {\n    if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {\n      [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];\n    }\n\n    const numerator1 = JSBI.leftShift(liquidity, JSBI.BigInt(96));\n    const numerator2 = JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96);\n\n    return roundUp\n      ? FullMath.mulDivRoundingUp(\n          FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),\n          ONE,\n          sqrtRatioAX96\n        )\n      : JSBI.divide(\n          JSBI.divide(JSBI.multiply(numerator1, numerator2), sqrtRatioBX96),\n          sqrtRatioAX96\n        );\n  }\n\n  static getAmount1DeltaWithRoundUp(\n    sqrtRatioAX96: JSBI,\n    sqrtRatioBX96: JSBI,\n    liquidity: JSBI,\n    roundUp: boolean\n  ): JSBI {\n    if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {\n      [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];\n    }\n\n    return roundUp\n      ? FullMath.mulDivRoundingUp(\n          liquidity,\n          JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96),\n          Q96\n        )\n      : JSBI.divide(\n          JSBI.multiply(liquidity, JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96)),\n          Q96\n        );\n  }\n\n  private static getNextSqrtPriceFromAmount0RoundingUp(\n    sqrtPX96: JSBI,\n    liquidity: JSBI,\n    amount: JSBI,\n    add: boolean\n  ): JSBI {\n    if (JSBI.equal(amount, ZERO)) return sqrtPX96;\n    const numerator1 = JSBI.leftShift(liquidity, JSBI.BigInt(96));\n\n    if (add) {\n      let product = multiplyIn256(amount, sqrtPX96);\n      if (JSBI.equal(JSBI.divide(product, amount), sqrtPX96)) {\n        const denominator = addIn256(numerator1, product);\n        if (JSBI.greaterThanOrEqual(denominator, numerator1)) {\n          return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator);\n        }\n      }\n\n      return FullMath.mulDivRoundingUp(\n        numerator1,\n        ONE,\n        JSBI.add(JSBI.divide(numerator1, sqrtPX96), amount)\n      );\n    } else {\n      let product = multiplyIn256(amount, sqrtPX96);\n\n      assert(JSBI.equal(JSBI.divide(product, amount), sqrtPX96));\n      assert(JSBI.greaterThan(numerator1, product));\n      const denominator = JSBI.subtract(numerator1, product);\n      return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator);\n    }\n  }\n\n  private static getNextSqrtPriceFromAmount1RoundingDown(\n    sqrtPX96: JSBI,\n    liquidity: JSBI,\n    amount: JSBI,\n    add: boolean\n  ): JSBI {\n    if (add) {\n      const quotient = JSBI.lessThanOrEqual(amount, MaxUint160)\n        ? JSBI.divide(JSBI.leftShift(amount, JSBI.BigInt(96)), liquidity)\n        : JSBI.divide(JSBI.multiply(amount, Q96), liquidity);\n\n      return JSBI.add(sqrtPX96, quotient);\n    } else {\n      const quotient = FullMath.mulDivRoundingUp(amount, Q96, liquidity);\n\n      assert(JSBI.greaterThan(sqrtPX96, quotient));\n      return JSBI.subtract(sqrtPX96, quotient);\n    }\n  }\n}\n"
  },
  {
    "path": "src/util/SwapMath.ts",
    "content": "import JSBI from \"jsbi\";\nimport { FullMath } from \"./FullMath\";\nimport { SqrtPriceMath } from \"./SqrtPriceMath\";\nimport { FeeAmount } from \"../enum/FeeAmount\";\nimport { ZERO, NEGATIVE_ONE, MAX_FEE } from \"../enum/InternalConstants\";\n\nexport abstract class SwapMath {\n  static computeSwapStep(\n    sqrtRatioCurrentX96: JSBI,\n    sqrtRatioTargetX96: JSBI,\n    liquidity: JSBI,\n    amountRemaining: JSBI,\n    feePips: FeeAmount\n  ): [JSBI, JSBI, JSBI, JSBI] {\n    const returnValues: Partial<{\n      sqrtRatioNextX96: JSBI;\n      amountIn: JSBI;\n      amountOut: JSBI;\n      feeAmount: JSBI;\n    }> = {};\n\n    const zeroForOne = JSBI.greaterThanOrEqual(\n      sqrtRatioCurrentX96,\n      sqrtRatioTargetX96\n    );\n    const exactIn = JSBI.greaterThanOrEqual(amountRemaining, ZERO);\n\n    if (exactIn) {\n      const amountRemainingLessFee = JSBI.divide(\n        JSBI.multiply(\n          amountRemaining,\n          JSBI.subtract(MAX_FEE, JSBI.BigInt(feePips))\n        ),\n        MAX_FEE\n      );\n      returnValues.amountIn = zeroForOne\n        ? SqrtPriceMath.getAmount0DeltaWithRoundUp(\n            sqrtRatioTargetX96,\n            sqrtRatioCurrentX96,\n            liquidity,\n            true\n          )\n        : SqrtPriceMath.getAmount1DeltaWithRoundUp(\n            sqrtRatioCurrentX96,\n            sqrtRatioTargetX96,\n            liquidity,\n            true\n          );\n      if (\n        JSBI.greaterThanOrEqual(amountRemainingLessFee, returnValues.amountIn!)\n      ) {\n        returnValues.sqrtRatioNextX96 = sqrtRatioTargetX96;\n      } else {\n        returnValues.sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput(\n          sqrtRatioCurrentX96,\n          liquidity,\n          amountRemainingLessFee,\n          zeroForOne\n        );\n      }\n    } else {\n      returnValues.amountOut = zeroForOne\n        ? SqrtPriceMath.getAmount1DeltaWithRoundUp(\n            sqrtRatioTargetX96,\n            sqrtRatioCurrentX96,\n            liquidity,\n            false\n          )\n        : SqrtPriceMath.getAmount0DeltaWithRoundUp(\n            sqrtRatioCurrentX96,\n            sqrtRatioTargetX96,\n            liquidity,\n            false\n          );\n      if (\n        JSBI.greaterThanOrEqual(\n          JSBI.multiply(amountRemaining, NEGATIVE_ONE),\n          returnValues.amountOut\n        )\n      ) {\n        returnValues.sqrtRatioNextX96 = sqrtRatioTargetX96;\n      } else {\n        returnValues.sqrtRatioNextX96 =\n          SqrtPriceMath.getNextSqrtPriceFromOutput(\n            sqrtRatioCurrentX96,\n            liquidity,\n            JSBI.multiply(amountRemaining, NEGATIVE_ONE),\n            zeroForOne\n          );\n      }\n    }\n\n    const max = JSBI.equal(sqrtRatioTargetX96, returnValues.sqrtRatioNextX96);\n\n    if (zeroForOne) {\n      returnValues.amountIn =\n        max && exactIn\n          ? returnValues.amountIn\n          : SqrtPriceMath.getAmount0DeltaWithRoundUp(\n              returnValues.sqrtRatioNextX96,\n              sqrtRatioCurrentX96,\n              liquidity,\n              true\n            );\n      returnValues.amountOut =\n        max && !exactIn\n          ? returnValues.amountOut\n          : SqrtPriceMath.getAmount1DeltaWithRoundUp(\n              returnValues.sqrtRatioNextX96,\n              sqrtRatioCurrentX96,\n              liquidity,\n              false\n            );\n    } else {\n      returnValues.amountIn =\n        max && exactIn\n          ? returnValues.amountIn\n          : SqrtPriceMath.getAmount1DeltaWithRoundUp(\n              sqrtRatioCurrentX96,\n              returnValues.sqrtRatioNextX96,\n              liquidity,\n              true\n            );\n      returnValues.amountOut =\n        max && !exactIn\n          ? returnValues.amountOut\n          : SqrtPriceMath.getAmount0DeltaWithRoundUp(\n              sqrtRatioCurrentX96,\n              returnValues.sqrtRatioNextX96,\n              liquidity,\n              false\n            );\n    }\n\n    if (\n      !exactIn &&\n      JSBI.greaterThan(\n        returnValues.amountOut!,\n        JSBI.multiply(amountRemaining, NEGATIVE_ONE)\n      )\n    ) {\n      returnValues.amountOut = JSBI.multiply(amountRemaining, NEGATIVE_ONE);\n    }\n\n    if (\n      exactIn &&\n      JSBI.notEqual(returnValues.sqrtRatioNextX96, sqrtRatioTargetX96)\n    ) {\n      // we didn't reach the target, so take the remainder of the maximum input as fee\n      returnValues.feeAmount = JSBI.subtract(\n        amountRemaining,\n        returnValues.amountIn!\n      );\n    } else {\n      returnValues.feeAmount = FullMath.mulDivRoundingUp(\n        returnValues.amountIn!,\n        JSBI.BigInt(feePips),\n        JSBI.subtract(MAX_FEE, JSBI.BigInt(feePips))\n      );\n    }\n\n    return [\n      returnValues.sqrtRatioNextX96!,\n      returnValues.amountIn!,\n      returnValues.amountOut!,\n      returnValues.feeAmount!,\n    ];\n  }\n}\n"
  },
  {
    "path": "src/util/TickMath.ts",
    "content": "import assert from \"assert\";\nimport JSBI from \"jsbi\";\nimport {\n  ZERO,\n  ONE,\n  TWO,\n  Q32,\n  MaxUint128,\n  MaxUint256,\n} from \"../enum/InternalConstants\";\n\nconst POWERS_OF_2 = [128, 64, 32, 16, 8, 4, 2, 1].map(\n  (pow: number): [number, JSBI] => [\n    pow,\n    JSBI.exponentiate(TWO, JSBI.BigInt(pow)),\n  ]\n);\n\nfunction mulShift(val: JSBI, mulBy: string): JSBI {\n  return JSBI.signedRightShift(\n    JSBI.multiply(val, JSBI.BigInt(mulBy)),\n    JSBI.BigInt(128)\n  );\n}\n\nexport abstract class TickMath {\n  /**\n   * The minimum tick that can be used on any pool.\n   */\n  static MIN_TICK: number = -887272;\n  /**\n   * The maximum tick that can be used on any pool.\n   */\n  static MAX_TICK: number = -TickMath.MIN_TICK;\n\n  /**\n   * The sqrt ratio corresponding to the minimum tick that could be used on any pool.\n   */\n  static MIN_SQRT_RATIO: JSBI = JSBI.BigInt(\"4295128739\");\n  /**\n   * The sqrt ratio corresponding to the maximum tick that could be used on any pool.\n   */\n  static MAX_SQRT_RATIO: JSBI = JSBI.BigInt(\n    \"1461446703485210103287273052203988822378723970342\"\n  );\n\n  /**\n   * Returns the sqrt ratio as a Q64.96 for the given tick. The sqrt ratio is computed as sqrt(1.0001)^tick\n   * @param tick the tick for which to compute the sqrt ratio\n   */\n  static getSqrtRatioAtTick(tick: number): JSBI {\n    assert(\n      tick >= TickMath.MIN_TICK &&\n        tick <= TickMath.MAX_TICK &&\n        Number.isInteger(tick),\n      \"TICK\"\n    );\n    const absTick: number = tick < 0 ? tick * -1 : tick;\n\n    let ratio: JSBI =\n      (absTick & 0x1) != 0\n        ? JSBI.BigInt(\"0xfffcb933bd6fad37aa2d162d1a594001\")\n        : JSBI.BigInt(\"0x100000000000000000000000000000000\");\n    if ((absTick & 0x2) != 0)\n      ratio = mulShift(ratio, \"0xfff97272373d413259a46990580e213a\");\n    if ((absTick & 0x4) != 0)\n      ratio = mulShift(ratio, \"0xfff2e50f5f656932ef12357cf3c7fdcc\");\n    if ((absTick & 0x8) != 0)\n      ratio = mulShift(ratio, \"0xffe5caca7e10e4e61c3624eaa0941cd0\");\n    if ((absTick & 0x10) != 0)\n      ratio = mulShift(ratio, \"0xffcb9843d60f6159c9db58835c926644\");\n    if ((absTick & 0x20) != 0)\n      ratio = mulShift(ratio, \"0xff973b41fa98c081472e6896dfb254c0\");\n    if ((absTick & 0x40) != 0)\n      ratio = mulShift(ratio, \"0xff2ea16466c96a3843ec78b326b52861\");\n    if ((absTick & 0x80) != 0)\n      ratio = mulShift(ratio, \"0xfe5dee046a99a2a811c461f1969c3053\");\n    if ((absTick & 0x100) != 0)\n      ratio = mulShift(ratio, \"0xfcbe86c7900a88aedcffc83b479aa3a4\");\n    if ((absTick & 0x200) != 0)\n      ratio = mulShift(ratio, \"0xf987a7253ac413176f2b074cf7815e54\");\n    if ((absTick & 0x400) != 0)\n      ratio = mulShift(ratio, \"0xf3392b0822b70005940c7a398e4b70f3\");\n    if ((absTick & 0x800) != 0)\n      ratio = mulShift(ratio, \"0xe7159475a2c29b7443b29c7fa6e889d9\");\n    if ((absTick & 0x1000) != 0)\n      ratio = mulShift(ratio, \"0xd097f3bdfd2022b8845ad8f792aa5825\");\n    if ((absTick & 0x2000) != 0)\n      ratio = mulShift(ratio, \"0xa9f746462d870fdf8a65dc1f90e061e5\");\n    if ((absTick & 0x4000) != 0)\n      ratio = mulShift(ratio, \"0x70d869a156d2a1b890bb3df62baf32f7\");\n    if ((absTick & 0x8000) != 0)\n      ratio = mulShift(ratio, \"0x31be135f97d08fd981231505542fcfa6\");\n    if ((absTick & 0x10000) != 0)\n      ratio = mulShift(ratio, \"0x9aa508b5b7a84e1c677de54f3e99bc9\");\n    if ((absTick & 0x20000) != 0)\n      ratio = mulShift(ratio, \"0x5d6af8dedb81196699c329225ee604\");\n    if ((absTick & 0x40000) != 0)\n      ratio = mulShift(ratio, \"0x2216e584f5fa1ea926041bedfe98\");\n    if ((absTick & 0x80000) != 0)\n      ratio = mulShift(ratio, \"0x48a170391f7dc42444e8fa2\");\n\n    if (tick > 0) ratio = JSBI.divide(MaxUint256, ratio);\n\n    // back to Q96\n    return JSBI.greaterThan(JSBI.remainder(ratio, Q32), ZERO)\n      ? JSBI.add(JSBI.divide(ratio, Q32), ONE)\n      : JSBI.divide(ratio, Q32);\n  }\n\n  /**\n   * Returns the tick corresponding to a given sqrt ratio, s.t. #getSqrtRatioAtTick(tick) <= sqrtRatioX96\n   * and #getSqrtRatioAtTick(tick + 1) > sqrtRatioX96\n   * @param sqrtRatioX96 the sqrt ratio as a Q64.96 for which to compute the tick\n   */\n  static getTickAtSqrtRatio(sqrtRatioX96: JSBI): number {\n    assert(\n      JSBI.greaterThanOrEqual(sqrtRatioX96, TickMath.MIN_SQRT_RATIO) &&\n        JSBI.lessThan(sqrtRatioX96, TickMath.MAX_SQRT_RATIO),\n      \"SQRT_RATIO\"\n    );\n\n    const sqrtRatioX128 = JSBI.leftShift(sqrtRatioX96, JSBI.BigInt(32));\n\n    const msb = TickMath.mostSignificantBit(sqrtRatioX128);\n\n    let r: JSBI;\n    if (JSBI.greaterThanOrEqual(JSBI.BigInt(msb), JSBI.BigInt(128))) {\n      r = JSBI.signedRightShift(sqrtRatioX128, JSBI.BigInt(msb - 127));\n    } else {\n      r = JSBI.leftShift(sqrtRatioX128, JSBI.BigInt(127 - msb));\n    }\n\n    let log_2: JSBI = JSBI.leftShift(\n      JSBI.subtract(JSBI.BigInt(msb), JSBI.BigInt(128)),\n      JSBI.BigInt(64)\n    );\n\n    for (let i = 0; i < 14; i++) {\n      r = JSBI.signedRightShift(JSBI.multiply(r, r), JSBI.BigInt(127));\n      const f = JSBI.signedRightShift(r, JSBI.BigInt(128));\n      log_2 = JSBI.bitwiseOr(log_2, JSBI.leftShift(f, JSBI.BigInt(63 - i)));\n      r = JSBI.signedRightShift(r, f);\n    }\n\n    const log_sqrt10001 = JSBI.multiply(\n      log_2,\n      JSBI.BigInt(\"255738958999603826347141\")\n    );\n\n    const tickLow = JSBI.toNumber(\n      JSBI.signedRightShift(\n        JSBI.subtract(\n          log_sqrt10001,\n          JSBI.BigInt(\"3402992956809132418596140100660247210\")\n        ),\n        JSBI.BigInt(128)\n      )\n    );\n    const tickHigh = JSBI.toNumber(\n      JSBI.signedRightShift(\n        JSBI.add(\n          log_sqrt10001,\n          JSBI.BigInt(\"291339464771989622907027621153398088495\")\n        ),\n        JSBI.BigInt(128)\n      )\n    );\n\n    return tickLow === tickHigh\n      ? tickLow\n      : JSBI.lessThanOrEqual(\n          TickMath.getSqrtRatioAtTick(tickHigh),\n          sqrtRatioX96\n        )\n      ? tickHigh\n      : tickLow;\n  }\n\n  static mostSignificantBit(x: JSBI): number {\n    assert(JSBI.greaterThan(x, ZERO), \"ZERO\");\n    assert(JSBI.lessThanOrEqual(x, MaxUint256), \"MAX\");\n\n    let msb: number = 0;\n    for (const [power, min] of POWERS_OF_2) {\n      if (JSBI.greaterThanOrEqual(x, min)) {\n        x = JSBI.signedRightShift(x, JSBI.BigInt(power));\n        msb += power;\n      }\n    }\n    return msb;\n  }\n\n  static tickSpacingToMaxLiquidityPerTick(tickSpacing: number): JSBI {\n    const minTick = JSBI.multiply(\n      JSBI.divide(JSBI.BigInt(this.MIN_TICK), JSBI.BigInt(tickSpacing)),\n      JSBI.BigInt(tickSpacing)\n    );\n    const maxTick = JSBI.multiply(\n      JSBI.divide(JSBI.BigInt(this.MAX_TICK), JSBI.BigInt(tickSpacing)),\n      JSBI.BigInt(tickSpacing)\n    );\n    const numTicks = JSBI.add(\n      JSBI.divide(JSBI.subtract(maxTick, minTick), JSBI.BigInt(tickSpacing)),\n      ONE\n    );\n    return JSBI.divide(MaxUint128, JSBI.BigInt(numTicks));\n  }\n}\n"
  },
  {
    "path": "src/util/index.ts",
    "content": "export * from \"./Serializer\";\nexport * from \"./DateConverter\";\nexport * from \"./FullMath\";\nexport * from \"./TickMath\";\nexport * from \"./LiquidityMath\";\nexport * from \"./DateUtils\";\nexport * from \"./BNUtils\";\nexport * from \"./FileUtils\";\n"
  },
  {
    "path": "test/ConfigurableCorePool.test.ts",
    "content": "import * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promised\";\nimport { FeeAmount } from \"../src/enum/FeeAmount\";\nimport { PoolConfig } from \"../src/model/PoolConfig\";\nimport { ConfigurableCorePool as IConfigurableCorePool } from \"../src/interface/ConfigurableCorePool\";\nimport { ConfigurableCorePool } from \"../src/core/ConfigurableCorePool\";\nimport { SQLiteSimulationDataManager } from \"../src/manager/SQLiteSimulationDataManager\";\nimport { PoolState } from \"../src/model/PoolState\";\nimport { SimulatorRoadmapManager } from \"../src/manager/SimulatorRoadmapManager\";\nimport { IdGenerator } from \"../src/util/IdGenerator\";\nimport { CorePool } from \"../src/core/CorePool\";\nimport { TickMath } from \"../src/util/TickMath\";\nimport JSBI from \"jsbi\";\nimport { ZERO } from \"../src/enum/InternalConstants\";\nimport { Transition } from \"../src/interface/Transition\";\nimport { getDate, getTomorrow, format } from \"../src/util/DateUtils\";\nimport { EventDBManager } from \"../src/manager/EventDBManager\";\nimport { EventType } from \"../src/enum/EventType\";\nimport { LiquidityEvent } from \"../src/entity/LiquidityEvent\";\nimport { SwapEvent } from \"../src/entity/SwapEvent\";\nimport { printParams } from \"../src/util/Serializer\";\nimport { SimulatorConsoleVisitor } from \"../src/manager/SimulatorConsoleVisitor\";\nimport { SimulatorPersistenceVisitor } from \"../src/manager/SimulatorPersistenceVisitor\";\nimport { SimulationDataManager } from \"../src/interface/SimulationDataManager\";\nchai.use(chaiAsPromised);\nconst expect = chai.expect;\nconst testUser = \"0x01\";\n\ndescribe(\"Test ConfigurableCorePool\", function () {\n  let simulationDataManager: SimulationDataManager;\n  let configurableCorePool: IConfigurableCorePool;\n  let simulatorRoadmapManager: SimulatorRoadmapManager;\n  let eventDB: EventDBManager;\n  let sqrtPriceX96ForInitialization = JSBI.BigInt(\n    \"0x43efef20f018fdc58e7a5cf0416a\"\n  );\n  let tickCurrentForInitialization = 195285;\n\n  async function getAndSortEventByDate(\n    startDate: Date,\n    endDate: Date\n  ): Promise<(LiquidityEvent | SwapEvent)[]> {\n    let events: (LiquidityEvent | SwapEvent)[] = [];\n    let mintEvents: LiquidityEvent[] = await eventDB.getLiquidityEventsByDate(\n      EventType.MINT,\n      format(startDate, \"yyyy-MM-dd HH:mm:ss\"),\n      format(endDate, \"yyyy-MM-dd HH:mm:ss\")\n    );\n    let burnEvents: LiquidityEvent[] = await eventDB.getLiquidityEventsByDate(\n      EventType.BURN,\n      format(startDate, \"yyyy-MM-dd HH:mm:ss\"),\n      format(endDate, \"yyyy-MM-dd HH:mm:ss\")\n    );\n    let swapEvents: SwapEvent[] = await eventDB.getSwapEventsByDate(\n      format(startDate, \"yyyy-MM-dd HH:mm:ss\"),\n      format(endDate, \"yyyy-MM-dd HH:mm:ss\")\n    );\n    events.push(...mintEvents);\n    events.push(...burnEvents);\n    events.push(...swapEvents);\n    events.sort(function (a, b) {\n      return a.blockNumber == b.blockNumber\n        ? a.logIndex - b.logIndex\n        : a.blockNumber - b.blockNumber;\n    });\n    return events;\n  }\n\n  async function replayEventsAndAssertReturnValues(\n    paramArr: (LiquidityEvent | SwapEvent)[]\n  ): Promise<void> {\n    for (let index = 0; index < paramArr.length; index++) {\n      // avoid stack overflow\n      if (index % 4000 == 0) {\n        configurableCorePool.takeSnapshot(\"\");\n      }\n\n      let param = paramArr[index];\n      let amount0: JSBI, amount1: JSBI;\n      switch (param.type) {\n        case EventType.MINT:\n          ({ amount0, amount1 } = await configurableCorePool.mint(\n            testUser,\n            param.tickLower,\n            param.tickUpper,\n            param.liquidity\n          ));\n          expect(amount0.toString()).to.eql(param.amount0.toString());\n          expect(amount1.toString()).to.eql(param.amount1.toString());\n          break;\n        case EventType.BURN:\n          ({ amount0, amount1 } = await configurableCorePool.burn(\n            testUser,\n            param.tickLower,\n            param.tickUpper,\n            param.liquidity\n          ));\n          expect(amount0.toString()).to.eql(param.amount0.toString());\n          expect(amount1.toString()).to.eql(param.amount1.toString());\n          break;\n        case EventType.SWAP:\n          // try-error to find `amountSpecified` and `sqrtPriceLimitX96` to resolve to the same result as swap event records\n          let errArr: Error[] = [];\n          if (\n            !JSBI.equal(\n              param.sqrtPriceX96,\n              configurableCorePool.getCorePool().sqrtPriceX96\n            )\n          ) {\n            if (\n              await tryAndSwap(errArr, param, param.amount0, param.sqrtPriceX96)\n            )\n              break;\n            else if (\n              await tryAndSwap(errArr, param, param.amount1, param.sqrtPriceX96)\n            )\n              break;\n          }\n          if (await tryAndSwap(errArr, param, param.amount0)) break;\n          else if (await tryAndSwap(errArr, param, param.amount1)) break;\n\n          if (errArr.length != 0) {\n            console.log(param.id);\n            return Promise.reject(\n              `Swap failed. Event index: ${index}. Event: ${printParams(\n                param\n              )}. ${errArr}`\n            );\n          }\n          break;\n        default:\n          // @ts-ignore: ExhaustiveCheck\n          const exhaustiveCheck: never = param;\n      }\n    }\n\n    async function swapWithDryRun(\n      errArr: Error[],\n      param: SwapEvent,\n      amountSpecified: JSBI,\n      sqrtPriceLimitX96?: JSBI\n    ): Promise<boolean> {\n      let zeroForOne: boolean = JSBI.greaterThan(param.amount0, ZERO)\n        ? true\n        : false;\n      let { amount0, amount1, sqrtPriceX96 } =\n        await configurableCorePool.querySwap(\n          zeroForOne,\n          amountSpecified,\n          sqrtPriceLimitX96\n        );\n      let dryRunRes =\n        JSBI.equal(amount0, param.amount0) &&\n        JSBI.equal(amount1, param.amount1) &&\n        JSBI.equal(sqrtPriceX96, param.sqrtPriceX96);\n\n      if (!dryRunRes) {\n        errArr.push(\n          new Error(\n            `Result is not match. amount0: ${amount0}, amount1: ${amount1}, actual sqrtPriceX96: ${sqrtPriceX96}`\n          )\n        );\n      }\n      return Promise.resolve(dryRunRes);\n    }\n\n    async function tryAndSwap(\n      errArr: Error[],\n      param: SwapEvent,\n      amountSpecified: JSBI,\n      sqrtPriceLimitX96?: JSBI\n    ): Promise<boolean> {\n      let zeroForOne: boolean = JSBI.greaterThan(param.amount0, ZERO)\n        ? true\n        : false;\n      let trySwapRes = await swapWithDryRun(\n        errArr,\n        param,\n        amountSpecified,\n        sqrtPriceLimitX96\n      );\n      if (trySwapRes) {\n        await configurableCorePool.swap(\n          zeroForOne,\n          amountSpecified,\n          sqrtPriceLimitX96\n        );\n\n        // add AmountSpecified column to swap event database\n        // await swapEventDB.addAmountSpecified(\n        //   param.id,\n        //   amountSpecified.toString()\n        // );\n      }\n      return trySwapRes;\n    }\n  }\n\n  beforeEach(async function () {\n    simulationDataManager = await SQLiteSimulationDataManager.buildInstance(\n      \"./test/database.db\"\n    );\n    eventDB = await EventDBManager.buildInstance(\n      \"events_0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8.db\"\n    );\n    simulatorRoadmapManager = new SimulatorRoadmapManager(\n      simulationDataManager\n    );\n    configurableCorePool = new ConfigurableCorePool(\n      new PoolState(new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)),\n      simulatorRoadmapManager,\n      new SimulatorConsoleVisitor(),\n      new SimulatorPersistenceVisitor(simulationDataManager)\n    );\n  });\n\n  afterEach(async function () {\n    await simulationDataManager.close();\n    await eventDB.close();\n  });\n\n  describe(\"Test get method\", function () {\n    it(\"can get id\", async function () {\n      expect(IdGenerator.validate(configurableCorePool.id)).to.be.true;\n    });\n\n    it(\"can get poolStateId\", async function () {\n      expect(IdGenerator.validate(configurableCorePool.getPoolState().id)).to.be\n        .true;\n    });\n\n    it(\"can get corePool\", async function () {\n      expect(configurableCorePool.getCorePool()).to.be.an.instanceOf(CorePool);\n    });\n  });\n\n  describe(\"Test initialize method\", function () {\n    it(\"can initialize\", async function () {\n      await configurableCorePool.initialize(sqrtPriceX96ForInitialization);\n      expect(configurableCorePool.getCorePool().tickCurrent).to.eql(\n        tickCurrentForInitialization\n      );\n      expect(configurableCorePool.getCorePool().sqrtPriceX96).to.eql(\n        sqrtPriceX96ForInitialization\n      );\n    });\n  });\n\n  describe(\"Test business interaction method\", function () {\n    beforeEach(async function () {\n      await configurableCorePool.initialize(sqrtPriceX96ForInitialization);\n    });\n\n    it(\"can mint\", async function () {\n      let liquidityToMint = JSBI.BigInt(\"10860507277202\");\n      await configurableCorePool.mint(\n        testUser,\n        192180,\n        193380,\n        liquidityToMint\n      );\n      expect(\n        configurableCorePool.getCorePool().getPosition(testUser, 192180, 193380)\n          .liquidity\n      ).to.eql(liquidityToMint);\n    });\n\n    it(\"can be interacted by user(mint, burn, swap)\", async function () {\n      let events = await getAndSortEventByDate(\n        getDate(2021, 5, 4),\n        getDate(2021, 5, 6)\n      );\n      return expect(replayEventsAndAssertReturnValues(events)).to.eventually.be\n        .fulfilled;\n    });\n\n    it(\"can be updated with PostProcessor by user\", async function () {\n      let count = 0;\n      configurableCorePool.updatePostProcessor(\n        (pool: IConfigurableCorePool, transition: Transition) => {\n          if (pool.getPoolState().id && transition.getRecord().id) count++;\n          return Promise.resolve();\n        }\n      );\n      let events = await getAndSortEventByDate(\n        getDate(2021, 5, 4),\n        getDate(2021, 5, 6)\n      );\n      await replayEventsAndAssertReturnValues(events);\n      expect(count).to.eql(events.length);\n    });\n  });\n\n  describe(\"Test state machine method\", function () {\n    beforeEach(async function () {\n      await configurableCorePool.initialize(sqrtPriceX96ForInitialization);\n    });\n\n    it(\"can fork\", async function () {\n      await configurableCorePool.mint(\n        testUser,\n        TickMath.MIN_TICK,\n        TickMath.MAX_TICK,\n        JSBI.BigInt(\"10860507277202\")\n      );\n      let liquidityAfterEvent1 = configurableCorePool.getCorePool().liquidity;\n      expect(JSBI.greaterThan(liquidityAfterEvent1, ZERO)).to.be.true;\n      let forkedConfigurableCorePool = configurableCorePool.fork();\n      expect(forkedConfigurableCorePool.getCorePool().tickCurrent).to.eql(\n        configurableCorePool.getCorePool().tickCurrent\n      );\n      await configurableCorePool.swap(true, JSBI.BigInt(\"1000000\"));\n      expect(forkedConfigurableCorePool.getCorePool().tickCurrent).to.not.eql(\n        configurableCorePool.getCorePool().tickCurrent\n      );\n    });\n\n    it(\"can take snapshot\", async function () {\n      await configurableCorePool.mint(\n        testUser,\n        TickMath.MIN_TICK,\n        TickMath.MAX_TICK,\n        JSBI.BigInt(\"10860507277202\")\n      );\n      configurableCorePool.takeSnapshot(\"for test\");\n      let snapshot = configurableCorePool.getPoolState().snapshot;\n      expect(snapshot).to.not.be.undefined;\n      expect(snapshot!.description).to.eql(\"for test\");\n      expect(\n        JSBI.equal(\n          snapshot!.liquidity,\n          configurableCorePool.getCorePool().liquidity\n        )\n      ).to.be.true;\n    });\n\n    it(\"can step back\", async function () {\n      await configurableCorePool.mint(\n        testUser,\n        TickMath.MIN_TICK,\n        TickMath.MAX_TICK,\n        JSBI.BigInt(\"10860507277202\")\n      );\n      let liquidityAfterEvent1 = configurableCorePool.getCorePool().liquidity;\n      expect(JSBI.greaterThan(liquidityAfterEvent1, ZERO)).to.be.true;\n      await configurableCorePool.mint(\n        testUser,\n        TickMath.MIN_TICK,\n        TickMath.MAX_TICK,\n        JSBI.BigInt(\"10860507277202\")\n      );\n      configurableCorePool.stepBack();\n      let liquidityAfterEvent2 = configurableCorePool.getCorePool().liquidity;\n      expect(JSBI.equal(liquidityAfterEvent1, liquidityAfterEvent2)).to.be.true;\n    });\n\n    it(\"can recover from snapshot\", async function () {\n      await configurableCorePool.mint(\n        testUser,\n        TickMath.MIN_TICK,\n        TickMath.MAX_TICK,\n        JSBI.BigInt(\"10860507277202\")\n      );\n      configurableCorePool.takeSnapshot(\"for test\");\n      let snapshot = configurableCorePool.getPoolState().snapshot;\n      expect(snapshot).to.not.be.undefined;\n      expect(snapshot!.description).to.eql(\"for test\");\n      expect(\n        JSBI.equal(\n          snapshot!.liquidity,\n          configurableCorePool.getCorePool().liquidity\n        )\n      ).to.be.true;\n\n      await configurableCorePool.mint(\n        testUser,\n        TickMath.MIN_TICK,\n        TickMath.MAX_TICK,\n        JSBI.BigInt(\"10860507277202\")\n      );\n      await configurableCorePool.swap(true, JSBI.BigInt(\"1000000\"));\n      expect(\n        JSBI.notEqual(\n          configurableCorePool.getCorePool().liquidity,\n          snapshot!.liquidity\n        )\n      ).to.be.true;\n      configurableCorePool.recover(snapshot!.id);\n      expect(\n        JSBI.equal(\n          configurableCorePool.getCorePool().liquidity,\n          snapshot!.liquidity\n        )\n      ).to.be.true;\n      expect(configurableCorePool.getPoolState().id).to.eql(snapshot!.id);\n\n      let newConfigurableCorePool = new ConfigurableCorePool(\n        new PoolState(new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)),\n        simulatorRoadmapManager,\n        new SimulatorConsoleVisitor(),\n        new SimulatorPersistenceVisitor(simulationDataManager)\n      );\n      newConfigurableCorePool.recover(snapshot!.id);\n      expect(\n        JSBI.equal(\n          snapshot!.liquidity,\n          newConfigurableCorePool.getCorePool().liquidity\n        )\n      ).to.be.true;\n      expect(configurableCorePool.getPoolState().id).to.eql(snapshot!.id);\n    });\n\n    it(\"can persist snapshot\", async function () {\n      await configurableCorePool.mint(\n        testUser,\n        TickMath.MIN_TICK,\n        TickMath.MAX_TICK,\n        JSBI.BigInt(\"10860507277202\")\n      );\n      configurableCorePool.takeSnapshot(\"for test\");\n      let snapshotId = await configurableCorePool.persistSnapshot();\n      let snapshotInPersistence = await simulationDataManager.getSnapshot(\n        snapshotId\n      );\n      expect(snapshotInPersistence).to.not.be.undefined;\n      expect(snapshotInPersistence!.id).to.eql(snapshotId);\n      expect(snapshotInPersistence!.description).to.eql(\"for test\");\n      expect(JSBI.greaterThan(snapshotInPersistence!.liquidity, ZERO)).to.be\n        .true;\n    });\n  });\n\n  describe(\"Test tricky method\", function () {\n    beforeEach(async function () {\n      await configurableCorePool.initialize(sqrtPriceX96ForInitialization);\n    });\n\n    it(\"can replay events on mainnet exactly as contract do\", async function () {\n      let startDate = getDate(2021, 5, 4);\n      let endDate = getDate(2021, 11, 6);\n      let currDate = startDate;\n\n      // configurableCorePool.updatePostProcessor(\n      //   (\n      //     configurableCorePool: IConfigurableCorePool,\n      //     transition: Transition\n      //   ) => {\n      //     if (\n      //       JSBI.equal(\n      //         configurableCorePool.getCorePool().sqrtPriceX96,\n      //         JSBI.BigInt(\"xxxxx\")\n      //       )\n      //     ) {\n      //       configurableCorePool.takeSnapshot(\n      //         \"for check error in event id xxx\"\n      //       );\n      //       return configurableCorePool\n      //         .persistSnapshot()\n      //         .then(() => Promise.resolve());\n      //     }\n      //     return Promise.resolve();\n      //   }\n      // );\n\n      while (currDate < endDate) {\n        let events = await getAndSortEventByDate(\n          currDate,\n          getTomorrow(currDate)\n        );\n        if (events.length > 0) {\n          await replayEventsAndAssertReturnValues(events);\n        }\n        currDate = getTomorrow(currDate);\n      }\n    });\n\n    it(\"can test any problem\", async function () {\n      let snapshot = await simulationDataManager.getSnapshot(\n        \"9577f400-5012-4492-8f1f-44c6dcb5980c\"\n      );\n      let testPool = new ConfigurableCorePool(\n        PoolState.from(snapshot!),\n        simulatorRoadmapManager,\n        new SimulatorConsoleVisitor(),\n        new SimulatorPersistenceVisitor(simulationDataManager)\n      );\n      let { amount0, amount1 } = await testPool.swap(\n        false,\n        JSBI.BigInt(\"500000000000000000000\")\n      );\n      console.log(\"amount0: \" + amount0.toString());\n      console.log(\"amount1: \" + amount1.toString());\n      console.log(\n        \"sqrtPriceX96: \" + testPool.getCorePool().sqrtPriceX96.toString()\n      );\n      console.log(\"tick: \" + testPool.getCorePool().tickCurrent);\n      console.log(\"liquidity: \" + testPool.getCorePool().liquidity.toString());\n    });\n  });\n});\n"
  },
  {
    "path": "test/JSBI.test.ts",
    "content": "import assert from \"assert\";\nimport JSBI from \"jsbi\";\nimport { TWO } from \"../src/enum/InternalConstants\";\n\ndescribe(\"Test JSBI\", function () {\n  it(\"division should rounds towards zero\", function () {\n    let a1 = JSBI.BigInt(-5);\n    let b1 = TWO;\n    assert.equal(JSBI.divide(a1, b1), -2);\n\n    let a2 = JSBI.BigInt(5);\n    let b2 = TWO;\n    assert.equal(JSBI.divide(a2, b2), 2);\n  });\n\n  it(\"notEqual in value can be replaced by !=\", function () {\n    let a1 = JSBI.BigInt(-5);\n    let b1 = TWO;\n    assert.ok(JSBI.notEqual(a1, b1));\n    assert.ok(a1 != b1);\n    assert.ok((a1 != b1) == JSBI.notEqual(a1, b1));\n  });\n});\n"
  },
  {
    "path": "test/LiquidityMath.test.ts",
    "content": "import { expect } from \"chai\";\nimport JSBI from \"jsbi\";\nimport { MaxUint256 } from \"../src/enum/InternalConstants\";\nimport { LiquidityMath } from \"../src/util/LiquidityMath\";\nimport { FullMath } from \"../src/util/FullMath\";\n\ndescribe(\"#maxLiquidityForAmounts\", () => {\n  const maxLiquidityForAmounts = LiquidityMath.maxLiquidityForAmounts;\n\n  function encodeSqrtRatioX96(amount1: number, amount0: number): JSBI {\n    const numerator = JSBI.leftShift(JSBI.BigInt(amount1), JSBI.BigInt(192));\n    const denominator = JSBI.BigInt(amount0);\n    const ratioX192 = JSBI.divide(numerator, denominator);\n    return FullMath.sqrt(ratioX192);\n  }\n\n  describe(\"imprecise\", () => {\n    describe(\"price inside\", () => {\n      it(\"100 token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(1, 1),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            JSBI.BigInt(\"200\"),\n            false\n          )\n        ).to.eql(JSBI.BigInt(2148));\n      });\n\n      it(\"100 token0, max token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(1, 1),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            MaxUint256,\n            false\n          )\n        ).to.eql(JSBI.BigInt(2148));\n      });\n\n      it(\"max token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(1, 1),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            MaxUint256,\n            JSBI.BigInt(\"200\"),\n            false\n          )\n        ).to.eql(JSBI.BigInt(4297));\n      });\n    });\n\n    describe(\"price below\", () => {\n      it(\"100 token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(99, 110),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            JSBI.BigInt(\"200\"),\n            false\n          )\n        ).to.eql(JSBI.BigInt(1048));\n      });\n\n      it(\"100 token0, max token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(99, 110),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            MaxUint256,\n            false\n          )\n        ).to.eql(JSBI.BigInt(1048));\n      });\n\n      it(\"max token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(99, 110),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            MaxUint256,\n            JSBI.BigInt(\"200\"),\n            false\n          )\n        ).to.eql(\n          JSBI.BigInt(\n            \"1214437677402050006470401421068302637228917309992228326090730924516431320489727\"\n          )\n        );\n      });\n    });\n\n    describe(\"price above\", () => {\n      it(\"100 token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(111, 100),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            JSBI.BigInt(\"200\"),\n            false\n          )\n        ).to.eql(JSBI.BigInt(2097));\n      });\n\n      it(\"100 token0, max token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(111, 100),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            MaxUint256,\n            false\n          )\n        ).to.eql(\n          JSBI.BigInt(\n            \"1214437677402050006470401421098959354205873606971497132040612572422243086574654\"\n          )\n        );\n      });\n\n      it(\"max token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(111, 100),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            MaxUint256,\n            JSBI.BigInt(\"200\"),\n            false\n          )\n        ).to.eql(JSBI.BigInt(2097));\n      });\n    });\n  });\n\n  describe(\"precise\", () => {\n    describe(\"price inside\", () => {\n      it(\"100 token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(1, 1),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            JSBI.BigInt(\"200\"),\n            true\n          )\n        ).to.eql(JSBI.BigInt(2148));\n      });\n\n      it(\"100 token0, max token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(1, 1),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            MaxUint256,\n            true\n          )\n        ).to.eql(JSBI.BigInt(2148));\n      });\n\n      it(\"max token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(1, 1),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            MaxUint256,\n            JSBI.BigInt(\"200\"),\n            true\n          )\n        ).to.eql(JSBI.BigInt(4297));\n      });\n    });\n\n    describe(\"price below\", () => {\n      it(\"100 token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(99, 110),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            JSBI.BigInt(\"200\"),\n            true\n          )\n        ).to.eql(JSBI.BigInt(1048));\n      });\n\n      it(\"100 token0, max token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(99, 110),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            MaxUint256,\n            true\n          )\n        ).to.eql(JSBI.BigInt(1048));\n      });\n\n      it(\"max token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(99, 110),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            MaxUint256,\n            JSBI.BigInt(\"200\"),\n            true\n          )\n        ).to.eql(\n          JSBI.BigInt(\n            \"1214437677402050006470401421082903520362793114274352355276488318240158678126184\"\n          )\n        );\n      });\n    });\n\n    describe(\"price above\", () => {\n      it(\"100 token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(111, 100),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            JSBI.BigInt(\"200\"),\n            true\n          )\n        ).to.eql(JSBI.BigInt(2097));\n      });\n\n      it(\"100 token0, max token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(111, 100),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            JSBI.BigInt(\"100\"),\n            MaxUint256,\n            true\n          )\n        ).to.eql(\n          JSBI.BigInt(\n            \"1214437677402050006470401421098959354205873606971497132040612572422243086574654\"\n          )\n        );\n      });\n\n      it(\"max token0, 200 token1\", () => {\n        expect(\n          maxLiquidityForAmounts(\n            encodeSqrtRatioX96(111, 100),\n            encodeSqrtRatioX96(100, 110),\n            encodeSqrtRatioX96(110, 100),\n            MaxUint256,\n            JSBI.BigInt(\"200\"),\n            true\n          )\n        ).to.eql(JSBI.BigInt(2097));\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/Serializer.test.ts",
    "content": "import assert from \"assert\";\nimport { PositionManager } from \"../src/manager/PositionManager\";\nimport { Position } from \"../src/model/Position\";\nimport { Serializer } from \"../src/util/Serializer\";\n\ndescribe(\"Test Serializer\", function () {\n  it(\"should do serialize/deserialize right\", function () {\n    let positionManager = new PositionManager();\n    positionManager.set(\"test\", new Position());\n    let json = Serializer.serialize(PositionManager, positionManager);\n    console.log(json);\n    assert.ok(json);\n\n    let object = Serializer.deserialize(PositionManager, json);\n    console.log(object);\n    assert.ok(object instanceof PositionManager);\n  });\n});\n"
  },
  {
    "path": "test/SimulationDataManager.test.ts",
    "content": "import { PoolState } from \"../src/model/PoolState\";\nimport { FeeAmount } from \"../src/enum/FeeAmount\";\nimport { ONE } from \"../src/enum/InternalConstants\";\nimport { TickManager } from \"../src/manager/TickManager\";\nimport { PositionManager } from \"../src/manager/PositionManager\";\nimport * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promised\";\nimport { SQLiteSimulationDataManager } from \"../src/manager/SQLiteSimulationDataManager\";\nimport { SimulationDataManager } from \"../src/interface/SimulationDataManager\";\nchai.use(chaiAsPromised);\nconst expect = chai.expect;\n\ndescribe(\"Test DBManager\", function () {\n  let simulationDataManager: SimulationDataManager;\n\n  beforeEach(async function () {\n    simulationDataManager = await SQLiteSimulationDataManager.buildInstance();\n  });\n\n  afterEach(async function () {\n    await simulationDataManager.close();\n  });\n\n  describe(\"can query when table is blank\", function () {\n    it(\"can getPoolConfig\", async function () {\n      return expect(simulationDataManager.getPoolConfig(\"123\")).to.eventually.be\n        .undefined;\n    });\n\n    it(\"can getSnapshot\", async function () {\n      return expect(simulationDataManager.getSnapshot(\"123\")).to.eventually.be\n        .undefined;\n    });\n\n    // since case here is corner case related to db damage, change DBManager.insertSnapshot to public then toggle below\n    // it(\"will throw error when get snapshot if pool config is missing\", async function () {\n    //   await db.insertSnapshot(\n    //     \"123\",\n    //     \"12345\",\n    //     \"for test\",\n    //     ONE,\n    //     ONE,\n    //     ONE,\n    //     ONE,\n    //     10,\n    //     ONE,\n    //     ONE,\n    //     new TickManager(),\n    //     new PositionManager(),\n    //     new Date()\n    //   );\n    //   return expect(db.getSnapshot(\"123\")).to.eventually.throw(\n    //     \"PoolConfig is of shortage!\"\n    //   );\n    // });\n  });\n\n  describe(\"can insert and query\", function () {\n    let poolConfig = {\n      id: \"1234\",\n      tickSpacing: 60,\n      token0: \"USDC\",\n      token1: \"ETH\",\n      fee: FeeAmount.MEDIUM,\n    };\n    let poolState = new PoolState(poolConfig);\n    let snapshotId = poolState.id;\n    poolState.takeSnapshot(\n      \"for test\",\n      ONE,\n      ONE,\n      ONE,\n      ONE,\n      10,\n      ONE,\n      ONE,\n      new TickManager(),\n      new PositionManager()\n    );\n\n    it(\"can persistSnapshot\", async function () {\n      return expect(simulationDataManager.persistSnapshot(poolState)).to\n        .eventually.be.fulfilled;\n    });\n\n    it(\"can getSnapshot\", async function () {\n      await simulationDataManager.persistSnapshot(poolState);\n      return expect(simulationDataManager.getSnapshot(snapshotId)).to.eventually\n        .be.not.undefined;\n    });\n\n    it(\"can getSnapshotProfiles\", async function () {\n      await simulationDataManager.persistSnapshot(poolState);\n      return expect(\n        simulationDataManager.getSnapshotProfiles()\n      ).to.eventually.have.lengthOf(1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/SimulatorClient.test.ts",
    "content": "import { SimulatorClient } from \"../src/client/SimulatorClient\";\nimport * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promised\";\nimport { SimulationDataManager } from \"../src/interface/SimulationDataManager\";\nimport { SQLiteSimulationDataManager } from \"../src/manager/SQLiteSimulationDataManager\";\nimport { FeeAmount } from \"../src/enum/FeeAmount\";\nimport { PoolConfig } from \"../src/model/PoolConfig\";\nimport { ConfigurableCorePool as IConfigurableCorePool } from \"../src/interface/ConfigurableCorePool\";\nimport { ConfigurableCorePool } from \"../src/core/ConfigurableCorePool\";\nimport JSBI from \"jsbi\";\nimport { EndBlockTypeWhenRecover } from \"../src/entity/EndBlockType\";\nimport { exists } from \"../src\";\nimport { EventDataSourceType } from \"../src/enum/EventDataSourceType\";\nchai.use(chaiAsPromised);\nconst expect = chai.expect;\n\ndescribe(\"Test SimulatorClient v2\", function () {\n  it(\"can download or update events and build the core pool at any block tag\", async function () {\n    let simulationDataManager: SimulationDataManager =\n      await SQLiteSimulationDataManager.buildInstance();\n    let clientInstance = new SimulatorClient(simulationDataManager);\n\n    let poolName = \"events-test\";\n    // case 1\n    // 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8\n    // 12374077\n    // case 2\n    // 0x92560C178cE069CC014138eD3C2F5221Ba71f58a\n    // 13578943\n\n    let poolAddress = \"0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8\";\n    let endBlock: EndBlockTypeWhenRecover = 12374077;\n    // Your customed RPCProviderUrl, or use config in tuner.config.js\n    let RPCProviderUrl: string | undefined = undefined;\n    // You can specify data source of events here, and Uniswap v3 Subgraph as default is recommended rather than RPC for at least 75% time saving.\n    // Just a reminder, RPC endpoint is necessary for the simulator even if you choose to download events from Subgraph.\n    let eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH;\n\n    if (!exists(`${poolName}_${poolAddress}.db`)) {\n      await clientInstance.initCorePoolFromMainnet(\n        poolName,\n        poolAddress,\n        \"afterDeployment\",\n        RPCProviderUrl,\n        eventDataSourceType\n      );\n    }\n\n    let configurableCorePool =\n      await clientInstance.recoverFromMainnetEventDBFile(\n        `${poolName}_${poolAddress}.db`,\n        endBlock,\n        RPCProviderUrl,\n        eventDataSourceType\n      );\n    console.log(`tick: ${configurableCorePool.getCorePool().tickCurrent}`);\n    console.log(\n      `sqrtPriceX96: ${configurableCorePool\n        .getCorePool()\n        .sqrtPriceX96.toString()}`\n    );\n\n    await clientInstance.shutdown();\n  });\n});\n\ndescribe(\"Test SimulatorClient static method\", function () {\n  it(\"can build instance\", async function () {\n    let simulationDataManager: SimulationDataManager =\n      await SQLiteSimulationDataManager.buildInstance();\n    let clientInstance = new SimulatorClient(simulationDataManager);\n    expect(clientInstance).to.be.an.instanceOf(SimulatorClient);\n    return expect(clientInstance.shutdown()).to.eventually.be.fulfilled;\n  });\n\n  it(\"can build PoolConfig\", async function () {\n    expect(\n      new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)\n    ).to.be.an.instanceOf(PoolConfig);\n  });\n});\n\ndescribe(\"Test SimulatorClient public method\", function () {\n  let clientInstance: SimulatorClient;\n\n  beforeEach(async function () {\n    let simulationDataManager: SimulationDataManager =\n      await SQLiteSimulationDataManager.buildInstance();\n    clientInstance = new SimulatorClient(simulationDataManager);\n  });\n\n  afterEach(async function () {\n    await clientInstance.shutdown();\n  });\n\n  it(\"can build ConfigurableCorePool instance\", async function () {\n    let configurableCorePool: IConfigurableCorePool =\n      clientInstance.initCorePoolFromConfig(\n        new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)\n      );\n    expect(configurableCorePool).to.be.an.instanceOf(ConfigurableCorePool);\n  });\n\n  describe(\"during simulation\", function () {\n    let configurableCorePool: IConfigurableCorePool;\n    let sqrtPriceX96ForInitialization = JSBI.BigInt(\"4295128739\");\n    beforeEach(async function () {\n      configurableCorePool = clientInstance.initCorePoolFromConfig(\n        new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)\n      );\n      await configurableCorePool.initialize(sqrtPriceX96ForInitialization);\n    });\n\n    it(\"can recover ConfigurableCorePool from a snapshot in persistence\", async function () {\n      let snapshotId = await configurableCorePool.persistSnapshot();\n      let recoveredConfigurableCorePool =\n        await clientInstance.recoverCorePoolFromSnapshot(snapshotId);\n      expect(recoveredConfigurableCorePool).to.be.an.instanceOf(\n        ConfigurableCorePool\n      );\n      expect(recoveredConfigurableCorePool.getCorePool().sqrtPriceX96).to.eql(\n        sqrtPriceX96ForInitialization\n      );\n    });\n\n    it(\"can list snapshot profiles\", async function () {\n      await configurableCorePool.persistSnapshot();\n      return expect(\n        clientInstance.listSnapshotProfiles()\n      ).to.eventually.have.lengthOf(1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/SimulatorRoadmapManager.test.ts",
    "content": "import * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promised\";\nimport { FeeAmount } from \"../src/enum/FeeAmount\";\nimport { PoolConfig } from \"../src/model/PoolConfig\";\nimport { ConfigurableCorePool as IConfigurableCorePool } from \"../src/interface/ConfigurableCorePool\";\nimport { ConfigurableCorePool } from \"../src/core/ConfigurableCorePool\";\nimport { PoolState } from \"../src/model/PoolState\";\nimport { SimulatorRoadmapManager } from \"../src/manager/SimulatorRoadmapManager\";\nimport { SimulatorRoadmapManager as ISimulatorRoadmapManager } from \"../src/interface/SimulatorRoadmapManager\";\nimport JSBI from \"jsbi\";\nimport { TickMath } from \"../src/util/TickMath\";\nimport { PoolStateHelper } from \"../src/util/PoolStateHelper\";\nimport * as sinon from \"sinon\";\nimport { SQLiteSimulationDataManager } from \"../src/manager/SQLiteSimulationDataManager\";\nimport { SimulatorConsoleVisitor } from \"../src/manager/SimulatorConsoleVisitor\";\nimport { SimulatorPersistenceVisitor } from \"../src/manager/SimulatorPersistenceVisitor\";\nimport { SimulationDataManager } from \"../src/interface/SimulationDataManager\";\n\nchai.use(chaiAsPromised);\nconst expect = chai.expect;\nconst testUser = \"0x01\";\n\ndescribe(\"Test SimulatorRoadmapManager\", function () {\n  let sandbox: sinon.SinonSandbox;\n  let consoleLogSpy: sinon.SinonSpy;\n  let simulationDataManager: SimulationDataManager;\n  let configurableCorePool: IConfigurableCorePool;\n  let simulatorRoadmapManager: ISimulatorRoadmapManager;\n  let sqrtPriceX96ForInitialization = JSBI.BigInt(\n    \"0x43efef20f018fdc58e7a5cf0416a\"\n  );\n\n  async function makeConfigurableCorePool(\n    simulatorRoadmapManager: SimulatorRoadmapManager\n  ): Promise<IConfigurableCorePool> {\n    configurableCorePool = new ConfigurableCorePool(\n      new PoolState(new PoolConfig(60, \"USDC\", \"ETH\", FeeAmount.MEDIUM)),\n      simulatorRoadmapManager,\n      new SimulatorConsoleVisitor(),\n      new SimulatorPersistenceVisitor(simulationDataManager)\n    );\n    await configurableCorePool.initialize(sqrtPriceX96ForInitialization);\n    await configurableCorePool.mint(\n      testUser,\n      TickMath.MIN_TICK,\n      TickMath.MAX_TICK,\n      JSBI.BigInt(\"10860507277202\")\n    );\n    await configurableCorePool.swap(true, JSBI.BigInt(\"1000000\"));\n    return Promise.resolve(configurableCorePool);\n  }\n\n  beforeEach(async function () {\n    sandbox = sinon.createSandbox();\n    consoleLogSpy = sandbox.spy(console, \"log\");\n    simulationDataManager = await SQLiteSimulationDataManager.buildInstance();\n    simulatorRoadmapManager = new SimulatorRoadmapManager(\n      simulationDataManager\n    );\n    configurableCorePool = await makeConfigurableCorePool(\n      simulatorRoadmapManager as SimulatorRoadmapManager\n    );\n  });\n\n  afterEach(async function () {\n    sandbox.restore();\n    await simulationDataManager.close();\n  });\n\n  describe(\"Test route method\", function () {\n    it(\"can print route\", async function () {\n      await simulatorRoadmapManager.printRoute(configurableCorePool.id);\n      // 8 for 1-poolConfig 3-transition 4-poolState\n      expect(consoleLogSpy.callCount).to.eql(8);\n    });\n\n    it(\"can persist route\", async function () {\n      let roadmapId = await simulatorRoadmapManager.persistRoute(\n        configurableCorePool.id,\n        \"for test\"\n      );\n      let roadmap = await simulationDataManager.getRoadmap(roadmapId);\n      expect(roadmap).to.not.be.undefined;\n      expect(roadmap!.snapshots).to.have.lengthOf(\n        PoolStateHelper.countHistoricalPoolStateTransitions(\n          configurableCorePool.getPoolState()\n        )\n      );\n    });\n\n    it(\"can list routes\", async function () {\n      configurableCorePool.fork();\n      await makeConfigurableCorePool(\n        simulatorRoadmapManager as SimulatorRoadmapManager\n      );\n      let pools = await simulatorRoadmapManager.listRoutes();\n      expect(pools).to.have.lengthOf(3);\n      expect(pools[0].id).to.be.not.eql(pools[1].id);\n      expect(pools[1].id).to.be.not.eql(pools[2].id);\n    });\n\n    it(\"load and print route\", async function () {\n      let roadmapId = await simulatorRoadmapManager.persistRoute(\n        configurableCorePool.id,\n        \"for test\"\n      );\n      await simulatorRoadmapManager.loadAndPrintRoute(roadmapId);\n      // 6 for 1-roadmap 1-poolConfig 4-poolState\n      expect(consoleLogSpy.callCount).to.eql(6);\n    });\n  });\n});\n"
  },
  {
    "path": "test/SwapMath.spec.ts",
    "content": "// import { BigNumber } from \"ethers\";\n// import { ethers } from \"hardhat\";\n// import { SwapMathTest } from \"../typechain/SwapMathTest\";\n\nimport { expect } from \"./shared/expect\";\n// import snapshotGasCost from \"./shared/snapshotGasCost\";\nimport { encodePriceSqrt, expandTo18Decimals } from \"./shared/utilities\";\n// import { SqrtPriceMathTest } from \"../typechain/SqrtPriceMathTest\";\nimport { ONE } from \"../src/enum/InternalConstants\";\nimport JSBI from \"jsbi\";\n// import { FeeAmount } from \"../src/enum/FeeAmount\";\nimport { SwapMath } from \"../src/util/SwapMath\";\nimport { SqrtPriceMath } from \"../src/util/SqrtPriceMath\";\n\ndescribe(\"SwapMath\", () => {\n  describe(\"#computeSwapStep\", () => {\n    it(\"exact amount in that gets capped at price target in one for zero\", async () => {\n      const price = encodePriceSqrt(ONE, ONE);\n      const priceTarget = encodePriceSqrt(JSBI.BigInt(101), JSBI.BigInt(100));\n      const liquidity = expandTo18Decimals(2);\n      const amount = expandTo18Decimals(1);\n      const fee = 600;\n      // const fee = FeeAmount.TEST;\n      const zeroForOne = false;\n\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          price,\n          priceTarget,\n          liquidity,\n          amount,\n          fee\n        );\n\n      expect(amountIn.toString()).to.eq(\"9975124224178055\");\n      expect(feeAmount.toString()).to.eq(\"5988667735148\");\n      expect(amountOut.toString()).to.eq(\"9925619580021728\");\n      expect(\n        JSBI.LT(JSBI.add(amountIn, feeAmount), amount),\n        \"entire amount is not used\"\n      ).to.be.true;\n\n      const priceAfterWholeInputAmount =\n        await SqrtPriceMath.getNextSqrtPriceFromInput(\n          price,\n          liquidity,\n          amount,\n          zeroForOne\n        );\n\n      expect(JSBI.EQ(sqrtQ, priceTarget), \"price is capped at price target\").to\n        .be.true;\n      expect(\n        JSBI.LT(sqrtQ, priceAfterWholeInputAmount),\n        \"price is less than price after whole input amount\"\n      ).to.be.true;\n    });\n\n    it(\"exact amount out that gets capped at price target in one for zero\", async () => {\n      const price = encodePriceSqrt(ONE, ONE);\n      const priceTarget = encodePriceSqrt(JSBI.BigInt(101), JSBI.BigInt(100));\n      const liquidity = expandTo18Decimals(2);\n      const amount = JSBI.multiply(expandTo18Decimals(1), JSBI.BigInt(-1));\n      const fee = 600;\n      const zeroForOne = false;\n\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          price,\n          priceTarget,\n          liquidity,\n          amount,\n          fee\n        );\n\n      expect(amountIn.toString()).to.eq(\"9975124224178055\");\n      expect(feeAmount.toString()).to.eq(\"5988667735148\");\n      expect(amountOut.toString()).to.eq(\"9925619580021728\");\n      expect(\n        JSBI.LT(amountOut, JSBI.multiply(amount, JSBI.BigInt(-1))),\n        \"entire amount out is not returned\"\n      ).to.be.true;\n\n      const priceAfterWholeOutputAmount =\n        await SqrtPriceMath.getNextSqrtPriceFromOutput(\n          price,\n          liquidity,\n          JSBI.multiply(amount, JSBI.BigInt(-1)),\n          zeroForOne\n        );\n\n      expect(sqrtQ.toString(), \"price is capped at price target\").to.eq(\n        priceTarget.toString()\n      );\n      expect(\n        JSBI.LT(sqrtQ, priceAfterWholeOutputAmount),\n        \"price is less than price after whole output amount\"\n      ).to.be.true;\n    });\n\n    it(\"exact amount in that is fully spent in one for zero\", async () => {\n      const price = encodePriceSqrt(ONE, ONE);\n      const priceTarget = encodePriceSqrt(JSBI.BigInt(1000), JSBI.BigInt(100));\n      const liquidity = expandTo18Decimals(2);\n      const amount = expandTo18Decimals(1);\n      const fee = 600;\n      const zeroForOne = false;\n\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          price,\n          priceTarget,\n          liquidity,\n          amount,\n          fee\n        );\n\n      expect(amountIn.toString()).to.eq(\"999400000000000000\");\n      expect(feeAmount.toString()).to.eq(\"600000000000000\");\n      expect(amountOut.toString()).to.eq(\"666399946655997866\");\n      expect(\n        JSBI.ADD(amountIn, feeAmount).toString(),\n        \"entire amount is used\"\n      ).to.eq(amount.toString());\n\n      const priceAfterWholeInputAmountLessFee =\n        await SqrtPriceMath.getNextSqrtPriceFromInput(\n          price,\n          liquidity,\n          JSBI.subtract(amount, feeAmount),\n          zeroForOne\n        );\n\n      expect(JSBI.LT(sqrtQ, priceTarget), \"price does not reach price target\")\n        .to.be.true;\n      expect(\n        sqrtQ.toString(),\n        \"price is equal to price after whole input amount\"\n      ).to.eq(priceAfterWholeInputAmountLessFee.toString());\n    });\n\n    it(\"exact amount out that is fully received in one for zero\", async () => {\n      const price = encodePriceSqrt(ONE, ONE);\n      const priceTarget = encodePriceSqrt(JSBI.BigInt(10000), JSBI.BigInt(100));\n      const liquidity = expandTo18Decimals(2);\n      const amount = JSBI.multiply(expandTo18Decimals(1), JSBI.BigInt(-1));\n      const fee = 600;\n      const zeroForOne = false;\n\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          price,\n          priceTarget,\n          liquidity,\n          amount,\n          fee\n        );\n\n      expect(amountIn.toString()).to.eq(\"2000000000000000000\");\n      expect(feeAmount.toString()).to.eq(\"1200720432259356\");\n      expect(amountOut.toString()).to.eq(\n        JSBI.multiply(amount, JSBI.BigInt(-1)).toString()\n      );\n\n      const priceAfterWholeOutputAmount =\n        await SqrtPriceMath.getNextSqrtPriceFromOutput(\n          price,\n          liquidity,\n          JSBI.multiply(amount, JSBI.BigInt(-1)),\n          zeroForOne\n        );\n\n      expect(JSBI.LT(sqrtQ, priceTarget), \"price does not reach price target\")\n        .to.be.true;\n      expect(\n        sqrtQ.toString(),\n        \"price is less than price after whole output amount\"\n      ).to.eq(priceAfterWholeOutputAmount.toString());\n    });\n\n    it(\"amount out is capped at the desired amount out\", async () => {\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          JSBI.BigInt(\"417332158212080721273783715441582\"),\n          JSBI.BigInt(\"1452870262520218020823638996\"),\n          JSBI.BigInt(\"159344665391607089467575320103\"),\n          JSBI.BigInt(\"-1\"),\n          1\n        );\n      expect(amountIn.toString()).to.eq(\"1\");\n      expect(feeAmount.toString()).to.eq(\"1\");\n      expect(amountOut.toString()).to.eq(\"1\"); // would be 2 if not capped\n      expect(sqrtQ.toString()).to.eq(\"417332158212080721273783715441581\");\n    });\n\n    it(\"target price of 1 uses partial input amount\", async () => {\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          JSBI.BigInt(\"2\"),\n          JSBI.BigInt(\"1\"),\n          JSBI.BigInt(\"1\"),\n          JSBI.BigInt(\"3915081100057732413702495386755767\"),\n          1\n        );\n      expect(amountIn.toString()).to.eq(\"39614081257132168796771975168\");\n      expect(feeAmount.toString()).to.eq(\"39614120871253040049813\");\n      expect(\n        JSBI.LE(\n          JSBI.add(amountIn, feeAmount),\n          \"3915081100057732413702495386755767\"\n        )\n      ).to.be.true;\n      expect(amountOut.toString()).to.eq(\"0\");\n      expect(sqrtQ.toString()).to.eq(\"1\");\n    });\n\n    it(\"entire input amount taken as fee\", async () => {\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          JSBI.BigInt(\"2413\"),\n          JSBI.BigInt(\"79887613182836312\"),\n          JSBI.BigInt(\"1985041575832132834610021537970\"),\n          JSBI.BigInt(\"10\"),\n          1872\n        );\n      expect(amountIn.toString()).to.eq(\"0\");\n      expect(feeAmount.toString()).to.eq(\"10\");\n      expect(amountOut.toString()).to.eq(\"0\");\n      expect(sqrtQ.toString()).to.eq(\"2413\");\n    });\n\n    it(\"handles intermediate insufficient liquidity in zero for one exact output case\", async () => {\n      const sqrtP = JSBI.BigInt(\"20282409603651670423947251286016\");\n      const sqrtPTarget = JSBI.divide(\n        JSBI.multiply(sqrtP, JSBI.BigInt(11)),\n        JSBI.BigInt(10)\n      );\n      const liquidity = JSBI.BigInt(1024);\n      // virtual reserves of one are only 4\n      // https://www.wolframalpha.com/input/?i=1024+%2F+%2820282409603651670423947251286016+%2F+2**96%29\n      const amountRemaining = JSBI.BigInt(-4);\n      const feePips = 3000;\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          sqrtP,\n          sqrtPTarget,\n          liquidity,\n          amountRemaining,\n          feePips\n        );\n      expect(amountOut.toString()).to.eq(\"0\");\n      expect(sqrtQ.toString()).to.eq(sqrtPTarget.toString());\n      expect(amountIn.toString()).to.eq(\"26215\");\n      expect(feeAmount.toString()).to.eq(\"79\");\n    });\n\n    it(\"handles intermediate insufficient liquidity in one for zero exact output case\", async () => {\n      const sqrtP = JSBI.BigInt(\"20282409603651670423947251286016\");\n      const sqrtPTarget = JSBI.divide(\n        JSBI.multiply(sqrtP, JSBI.BigInt(9)),\n        JSBI.BigInt(10)\n      );\n      const liquidity = JSBI.BigInt(1024);\n      // virtual reserves of zero are only 262144\n      // https://www.wolframalpha.com/input/?i=1024+*+%2820282409603651670423947251286016+%2F+2**96%29\n      const amountRemaining = JSBI.BigInt(-263000);\n      const feePips = 3000;\n      const [sqrtQ, amountIn, amountOut, feeAmount] =\n        await SwapMath.computeSwapStep(\n          sqrtP,\n          sqrtPTarget,\n          liquidity,\n          amountRemaining,\n          feePips\n        );\n      expect(amountOut.toString()).to.eq(\"26214\");\n      expect(sqrtQ.toString()).to.eq(sqrtPTarget.toString());\n      expect(amountIn.toString()).to.eq(\"1\");\n      expect(feeAmount.toString()).to.eq(\"1\");\n    });\n\n    // describe('gas', () => {\n    //   it('swap one for zero exact in capped', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(101, 100),\n    //         expandTo18Decimals(2),\n    //         expandTo18Decimals(1),\n    //         600\n    //       )\n    //     )\n    //   })\n    //   it('swap zero for one exact in capped', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(99, 100),\n    //         expandTo18Decimals(2),\n    //         expandTo18Decimals(1),\n    //         600\n    //       )\n    //     )\n    //   })\n    //   it('swap one for zero exact out capped', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(101, 100),\n    //         expandTo18Decimals(2),\n    //         expandTo18Decimals(1).mul(-1),\n    //         600\n    //       )\n    //     )\n    //   })\n    //   it('swap zero for one exact out capped', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(99, 100),\n    //         expandTo18Decimals(2),\n    //         expandTo18Decimals(1).mul(-1),\n    //         600\n    //       )\n    //     )\n    //   })\n    //   it('swap one for zero exact in partial', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(1010, 100),\n    //         expandTo18Decimals(2),\n    //         1000,\n    //         600\n    //       )\n    //     )\n    //   })\n    //   it('swap zero for one exact in partial', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(99, 1000),\n    //         expandTo18Decimals(2),\n    //         1000,\n    //         600\n    //       )\n    //     )\n    //   })\n    //   it('swap one for zero exact out partial', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(1010, 100),\n    //         expandTo18Decimals(2),\n    //         1000,\n    //         600\n    //       )\n    //     )\n    //   })\n    //   it('swap zero for one exact out partial', async () => {\n    //     await snapshotGasCost(\n    //       swapMath.getGasCostOfComputeSwapStep(\n    //         encodePriceSqrt(1, 1),\n    //         encodePriceSqrt(99, 1000),\n    //         expandTo18Decimals(2),\n    //         1000,\n    //         600\n    //       )\n    //     )\n    //   })\n    // })\n  });\n});\n"
  },
  {
    "path": "test/TestSubgraph.test.ts",
    "content": "import { request, gql } from \"graphql-request\";\nimport * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promised\";\nchai.use(chaiAsPromised);\n\ndescribe(\"Test Uniswap v3 Subgraph\", function () {\n  const APIURL = \"https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3\";\n\n  it(\"can download events\", async function () {\n    const query = gql`\n      query {\n        pool(id: \"0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8\") {\n          id\n          mints(first: 5, orderBy: timestamp, orderDirection: asc) {\n            amount0\n            amount1\n            amount\n            logIndex\n            timestamp\n          }\n        }\n      }\n    `;\n\n    let data = await request(APIURL, query);\n    console.log(data.pool.mints);\n  });\n});\n"
  },
  {
    "path": "test/Tick.spec.ts",
    "content": "import { TickTest } from \"./stubs/TickTest\";\nimport { expect } from \"./shared/expect\";\nimport {\n  FeeAmount,\n  getMaxLiquidityPerTick,\n  TICK_SPACINGS,\n} from \"./shared/utilities\";\nimport { TickManager } from \"../src/manager/TickManager\";\nimport {\n  MaxUint128,\n  MaxUint256,\n  ONE,\n  TWO,\n  ZERO,\n} from \"../src/enum/InternalConstants\";\nimport JSBI from \"jsbi\";\nimport { Tick } from \"../src/model/Tick\";\n\ndescribe(\"Tick\", () => {\n  let tickManager: TickManager;\n  let tickTest: TickTest;\n\n  beforeEach(\"deploy TickTest\", async () => {\n    tickManager = new TickManager();\n    tickTest = new TickTest(tickManager);\n  });\n\n  describe(\"#tickSpacingToMaxLiquidityPerTick\", () => {\n    it(\"returns the correct value for low fee\", async () => {\n      const maxLiquidityPerTick =\n        await tickTest.tickSpacingToMaxLiquidityPerTick(\n          TICK_SPACINGS[FeeAmount.LOW]\n        );\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        \"1917569901783203986719870431555990\"\n      ); // 110.8 bits\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.LOW]).toString()\n      );\n    });\n    it(\"returns the correct value for medium fee\", async () => {\n      const maxLiquidityPerTick =\n        await tickTest.tickSpacingToMaxLiquidityPerTick(\n          TICK_SPACINGS[FeeAmount.MEDIUM]\n        );\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        \"11505743598341114571880798222544994\"\n      ); // 113.1 bits\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.MEDIUM]).toString()\n      );\n    });\n    it(\"returns the correct value for high fee\", async () => {\n      const maxLiquidityPerTick =\n        await tickTest.tickSpacingToMaxLiquidityPerTick(\n          TICK_SPACINGS[FeeAmount.HIGH]\n        );\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        \"38350317471085141830651933667504588\"\n      ); // 114.7 bits\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.HIGH]).toString()\n      );\n    });\n    it(\"returns the correct value for entire range\", async () => {\n      const maxLiquidityPerTick =\n        await tickTest.tickSpacingToMaxLiquidityPerTick(887272);\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        JSBI.divide(MaxUint128, JSBI.BigInt(3)).toString()\n      ); // 126 bits\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        getMaxLiquidityPerTick(887272).toString()\n      );\n    });\n    it(\"returns the correct value for 2302\", async () => {\n      const maxLiquidityPerTick =\n        await tickTest.tickSpacingToMaxLiquidityPerTick(2302);\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        \"441351967472034323558203122479595605\"\n      ); // 118 bits\n      expect(maxLiquidityPerTick.toString()).to.eql(\n        getMaxLiquidityPerTick(2302).toString()\n      );\n    });\n  });\n\n  describe(\"#getFeeGrowthInside\", () => {\n    beforeEach(\"initialize 2 ticks: 2 and -2\", async () => {\n      tickManager.set(new Tick(-2));\n      tickManager.set(new Tick(2));\n    });\n    it(\"returns all for two uninitialized ticks if tick is inside\", async () => {\n      const { feeGrowthInside0X128, feeGrowthInside1X128 } =\n        await tickTest.getFeeGrowthInside(\n          -2,\n          2,\n          0,\n          JSBI.BigInt(15),\n          JSBI.BigInt(15)\n        );\n      expect(feeGrowthInside0X128.toString()).to.eql(\"15\");\n      expect(feeGrowthInside1X128.toString()).to.eql(\"15\");\n    });\n    it(\"returns 0 for two uninitialized ticks if tick is above\", async () => {\n      const { feeGrowthInside0X128, feeGrowthInside1X128 } =\n        await tickTest.getFeeGrowthInside(\n          -2,\n          2,\n          4,\n          JSBI.BigInt(15),\n          JSBI.BigInt(15)\n        );\n      expect(feeGrowthInside0X128.toString()).to.eql(\"0\");\n      expect(feeGrowthInside1X128.toString()).to.eql(\"0\");\n    });\n    it(\"returns 0 for two uninitialized ticks if tick is below\", async () => {\n      const { feeGrowthInside0X128, feeGrowthInside1X128 } =\n        await tickTest.getFeeGrowthInside(\n          -2,\n          2,\n          -4,\n          JSBI.BigInt(15),\n          JSBI.BigInt(15)\n        );\n      expect(feeGrowthInside0X128.toString()).to.eql(\"0\");\n      expect(feeGrowthInside1X128.toString()).to.eql(\"0\");\n    });\n\n    it(\"subtracts upper tick if below\", async () => {\n      await tickTest.setTick(2, {\n        feeGrowthOutside0X128: TWO,\n        feeGrowthOutside1X128: JSBI.BigInt(3),\n        liquidityGross: ZERO,\n        liquidityNet: ZERO,\n      });\n      const { feeGrowthInside0X128, feeGrowthInside1X128 } =\n        await tickTest.getFeeGrowthInside(\n          -2,\n          2,\n          0,\n          JSBI.BigInt(15),\n          JSBI.BigInt(15)\n        );\n      expect(feeGrowthInside0X128.toString()).to.eql(\"13\");\n      expect(feeGrowthInside1X128.toString()).to.eql(\"12\");\n    });\n\n    it(\"subtracts lower tick if above\", async () => {\n      await tickTest.setTick(-2, {\n        feeGrowthOutside0X128: TWO,\n        feeGrowthOutside1X128: JSBI.BigInt(3),\n        liquidityGross: ZERO,\n        liquidityNet: ZERO,\n      });\n      const { feeGrowthInside0X128, feeGrowthInside1X128 } =\n        await tickTest.getFeeGrowthInside(\n          -2,\n          2,\n          0,\n          JSBI.BigInt(15),\n          JSBI.BigInt(15)\n        );\n      expect(feeGrowthInside0X128.toString()).to.eql(\"13\");\n      expect(feeGrowthInside1X128.toString()).to.eql(\"12\");\n    });\n\n    it(\"subtracts upper and lower tick if inside\", async () => {\n      await tickTest.setTick(-2, {\n        feeGrowthOutside0X128: TWO,\n        feeGrowthOutside1X128: JSBI.BigInt(3),\n        liquidityGross: ZERO,\n        liquidityNet: ZERO,\n      });\n      await tickTest.setTick(2, {\n        feeGrowthOutside0X128: JSBI.BigInt(4),\n        feeGrowthOutside1X128: JSBI.BigInt(1),\n        liquidityGross: ZERO,\n        liquidityNet: ZERO,\n      });\n      const { feeGrowthInside0X128, feeGrowthInside1X128 } =\n        await tickTest.getFeeGrowthInside(\n          -2,\n          2,\n          0,\n          JSBI.BigInt(15),\n          JSBI.BigInt(15)\n        );\n      expect(feeGrowthInside0X128.toString()).to.eql(\"9\");\n      expect(feeGrowthInside1X128.toString()).to.eql(\"11\");\n    });\n\n    it(\"works correctly with overflow on inside tick\", async () => {\n      await tickTest.setTick(-2, {\n        feeGrowthOutside0X128: JSBI.subtract(MaxUint256, JSBI.BigInt(3)),\n        feeGrowthOutside1X128: JSBI.subtract(MaxUint256, TWO),\n        liquidityGross: ZERO,\n        liquidityNet: ZERO,\n      });\n      await tickTest.setTick(2, {\n        feeGrowthOutside0X128: JSBI.BigInt(3),\n        feeGrowthOutside1X128: JSBI.BigInt(5),\n        liquidityGross: ZERO,\n        liquidityNet: ZERO,\n      });\n      const { feeGrowthInside0X128, feeGrowthInside1X128 } =\n        await tickTest.getFeeGrowthInside(\n          -2,\n          2,\n          0,\n          JSBI.BigInt(15),\n          JSBI.BigInt(15)\n        );\n      expect(feeGrowthInside0X128.toString()).to.eql(\"16\");\n      expect(feeGrowthInside1X128.toString()).to.eql(\"13\");\n    });\n  });\n\n  describe(\"#update\", async () => {\n    beforeEach(\"initialize 3 ticks: 0, 1, and 2\", async () => {\n      tickManager.set(new Tick(0));\n      tickManager.set(new Tick(1));\n      tickManager.set(new Tick(2));\n    });\n    it(\"flips from zero to nonzero\", async () => {\n      expect(\n        await tickTest.update(0, 0, ONE, ZERO, ZERO, false, JSBI.BigInt(3))\n      ).to.eql(true);\n    });\n    it(\"does not flip from nonzero to greater nonzero\", async () => {\n      await tickTest.update(0, 0, ONE, ZERO, ZERO, false, JSBI.BigInt(3));\n      expect(\n        await tickTest.update(0, 0, ONE, ZERO, ZERO, false, JSBI.BigInt(3))\n      ).to.eql(false);\n    });\n    it(\"flips from nonzero to zero\", async () => {\n      await tickTest.update(0, 0, ONE, ZERO, ZERO, false, JSBI.BigInt(3));\n      expect(\n        await tickTest.update(\n          0,\n          0,\n          JSBI.unaryMinus(ONE),\n          ZERO,\n          ZERO,\n          false,\n          JSBI.BigInt(3)\n        )\n      ).to.eql(true);\n    });\n    it(\"does not flip from nonzero to lesser nonzero\", async () => {\n      await tickTest.update(0, 0, TWO, ZERO, ZERO, false, JSBI.BigInt(3));\n      expect(\n        await tickTest.update(\n          0,\n          0,\n          JSBI.unaryMinus(ONE),\n          ZERO,\n          ZERO,\n          false,\n          JSBI.BigInt(3)\n        )\n      ).to.eql(false);\n    });\n    it(\"does not flip from nonzero to lesser nonzero\", async () => {\n      await tickTest.update(0, 0, TWO, ZERO, ZERO, false, JSBI.BigInt(3));\n      expect(\n        await tickTest.update(\n          0,\n          0,\n          JSBI.unaryMinus(ONE),\n          ZERO,\n          ZERO,\n          false,\n          JSBI.BigInt(3)\n        )\n      ).to.eql(false);\n    });\n    it(\"reverts if total liquidity gross is greater than max\", async () => {\n      await tickTest.update(0, 0, TWO, ZERO, ZERO, false, JSBI.BigInt(3));\n      await tickTest.update(0, 0, ONE, ZERO, ZERO, true, JSBI.BigInt(3));\n      expect(() =>\n        tickTest.update(0, 0, ONE, ZERO, ZERO, false, JSBI.BigInt(3))\n      ).to.throw(\"LO\");\n    });\n    it(\"nets the liquidity based on upper flag\", async () => {\n      await tickTest.update(0, 0, TWO, ZERO, ZERO, false, JSBI.BigInt(10));\n      await tickTest.update(0, 0, ONE, ZERO, ZERO, true, JSBI.BigInt(10));\n      await tickTest.update(\n        0,\n        0,\n        JSBI.BigInt(3),\n        ZERO,\n        ZERO,\n        true,\n        JSBI.BigInt(10)\n      );\n      await tickTest.update(0, 0, ONE, ZERO, ZERO, false, JSBI.BigInt(10));\n      const { liquidityGross, liquidityNet } = await tickTest.ticks(0);\n      expect(liquidityGross.toString()).to.eql((2 + 1 + 3 + 1).toString());\n      expect(liquidityNet.toString()).to.eql((2 - 1 - 3 + 1).toString());\n    });\n    it(\"reverts on overflow liquidity net\", async () => {\n      await tickTest.update(\n        0,\n        0,\n        JSBI.subtract(JSBI.divide(MaxUint128, TWO), ONE),\n        ZERO,\n        ZERO,\n        false,\n        MaxUint128\n      );\n      expect(() =>\n        tickTest.update(\n          0,\n          0,\n          JSBI.subtract(JSBI.divide(MaxUint128, TWO), ONE),\n          ZERO,\n          ZERO,\n          false,\n          MaxUint128\n        )\n      ).to.throw();\n    });\n    it(\"assumes all growth happens below ticks lte current tick\", async () => {\n      await tickTest.update(1, 1, ONE, ONE, TWO, false, MaxUint128);\n      const { feeGrowthOutside0X128, feeGrowthOutside1X128, initialized } =\n        await tickTest.ticks(1);\n      expect(feeGrowthOutside0X128.toString()).to.eql(\"1\");\n      expect(feeGrowthOutside1X128.toString()).to.eql(\"2\");\n      expect(initialized).to.eql(true);\n    });\n    it(\"does not set any growth fields if tick is already initialized\", async () => {\n      await tickTest.update(1, 1, ONE, ONE, TWO, false, MaxUint128);\n      await tickTest.update(\n        1,\n        1,\n        ONE,\n        JSBI.BigInt(6),\n        JSBI.BigInt(7),\n        false,\n        MaxUint128\n      );\n      const { feeGrowthOutside0X128, feeGrowthOutside1X128, initialized } =\n        await tickTest.ticks(1);\n      expect(feeGrowthOutside0X128.toString()).to.eql(\"1\");\n      expect(feeGrowthOutside1X128.toString()).to.eql(\"2\");\n      expect(initialized).to.eql(true);\n    });\n    it(\"does not set any growth fields for ticks gt current tick\", async () => {\n      await tickTest.update(2, 1, ONE, ONE, TWO, false, MaxUint128);\n      const { feeGrowthOutside0X128, feeGrowthOutside1X128, initialized } =\n        await tickTest.ticks(2);\n      expect(feeGrowthOutside0X128.toString()).to.eql(\"0\");\n      expect(feeGrowthOutside1X128.toString()).to.eql(\"0\");\n      expect(initialized).to.eql(true);\n    });\n  });\n\n  describe(\"#clear\", async () => {\n    beforeEach(\"initialize 1 tick: 2\", async () => {\n      tickManager.set(new Tick(2));\n    });\n    it(\"deletes all the data in the tick\", async () => {\n      await tickTest.setTick(2, {\n        feeGrowthOutside0X128: ONE,\n        feeGrowthOutside1X128: TWO,\n        liquidityGross: JSBI.BigInt(3),\n        liquidityNet: JSBI.BigInt(4),\n      });\n      await tickTest.clear(2);\n      const {\n        feeGrowthOutside0X128,\n        feeGrowthOutside1X128,\n        liquidityGross,\n        liquidityNet,\n        initialized,\n      } = await tickTest.ticks(2);\n      expect(feeGrowthOutside0X128.toString()).to.eql(\"0\");\n      expect(feeGrowthOutside1X128.toString()).to.eql(\"0\");\n      expect(liquidityGross.toString()).to.eql(\"0\");\n      expect(liquidityNet.toString()).to.eql(\"0\");\n      expect(initialized).to.eql(false);\n    });\n  });\n\n  describe(\"#cross\", () => {\n    beforeEach(\"initialize 1 tick: 2\", async () => {\n      tickManager.set(new Tick(2));\n    });\n    it(\"flips the growth variables\", async () => {\n      await tickTest.setTick(2, {\n        feeGrowthOutside0X128: ONE,\n        feeGrowthOutside1X128: TWO,\n        liquidityGross: JSBI.BigInt(3),\n        liquidityNet: JSBI.BigInt(4),\n      });\n      await tickTest.cross(2, JSBI.BigInt(7), JSBI.BigInt(9));\n      const { feeGrowthOutside0X128, feeGrowthOutside1X128 } =\n        await tickTest.ticks(2);\n      expect(feeGrowthOutside0X128.toString()).to.eql(\"6\");\n      expect(feeGrowthOutside1X128.toString()).to.eql(\"7\");\n    });\n    it(\"two flips are no op\", async () => {\n      await tickTest.setTick(2, {\n        feeGrowthOutside0X128: ONE,\n        feeGrowthOutside1X128: TWO,\n        liquidityGross: JSBI.BigInt(3),\n        liquidityNet: JSBI.BigInt(4),\n      });\n      await tickTest.cross(2, JSBI.BigInt(7), JSBI.BigInt(9));\n      await tickTest.cross(2, JSBI.BigInt(7), JSBI.BigInt(9));\n      const { feeGrowthOutside0X128, feeGrowthOutside1X128 } =\n        await tickTest.ticks(2);\n      expect(feeGrowthOutside0X128.toString()).to.eql(\"1\");\n      expect(feeGrowthOutside1X128.toString()).to.eql(\"2\");\n    });\n  });\n});\n"
  },
  {
    "path": "test/TickManager.test.ts",
    "content": "import { Tick } from \"../src/model/Tick\";\nimport { TickMath } from \"../src/util/TickMath\";\nimport { expect } from \"./shared/expect\";\nimport { TickManager } from \"../src/manager/TickManager\";\n\ndescribe(\"TickManager\", () => {\n  describe(\"#getNextInitializedTick\", () => {\n    let highTick: Tick;\n    let lowTick: Tick;\n    let midTick: Tick;\n    let ticks: Tick[];\n    let tickManager: TickManager = new TickManager();\n\n    beforeEach(() => {\n      lowTick = new Tick(TickMath.MIN_TICK + 1);\n      midTick = new Tick(0);\n      highTick = new Tick(TickMath.MAX_TICK - 1);\n\n      ticks = [lowTick, midTick, highTick];\n\n      for (let tick of ticks) {\n        tickManager.set(tick);\n      }\n    });\n\n    it(\"lte = true\", () => {\n      expect(tickManager.getNextInitializedTick(-257, 1, true)).to.eql({\n        nextTick: -512,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(-256, 1, true)).to.eql({\n        nextTick: -256,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(-1, 1, true)).to.eql({\n        nextTick: -256,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(0, 1, true)).to.eql({\n        nextTick: 0,\n        initialized: true,\n      });\n      expect(tickManager.getNextInitializedTick(1, 1, true)).to.eql({\n        nextTick: 0,\n        initialized: true,\n      });\n      expect(tickManager.getNextInitializedTick(255, 1, true)).to.eql({\n        nextTick: 0,\n        initialized: true,\n      });\n      expect(tickManager.getNextInitializedTick(256, 1, true)).to.eql({\n        nextTick: 256,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(257, 1, true)).to.eql({\n        nextTick: 256,\n        initialized: false,\n      });\n    });\n\n    it(\"lte = false\", () => {\n      expect(tickManager.getNextInitializedTick(-215041, 60, false)).to.eql({\n        nextTick: -199740,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(-257, 1, false)).to.eql({\n        nextTick: -1,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(-256, 1, false)).to.eql({\n        nextTick: -1,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(-2, 1, false)).to.eql({\n        nextTick: -1,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(-1, 1, false)).to.eql({\n        nextTick: 0,\n        initialized: true,\n      });\n      expect(tickManager.getNextInitializedTick(0, 1, false)).to.eql({\n        nextTick: 255,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(1, 1, false)).to.eql({\n        nextTick: 255,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(254, 1, false)).to.eql({\n        nextTick: 255,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(255, 1, false)).to.eql({\n        nextTick: 511,\n        initialized: false,\n      });\n      expect(tickManager.getNextInitializedTick(256, 1, false)).to.eql({\n        nextTick: 511,\n        initialized: false,\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/TickMath.test.ts",
    "content": "import { expect } from \"chai\";\nimport JSBI from \"jsbi\";\nimport { ONE, MaxUint256, TWO } from \"../src/enum/InternalConstants\";\nimport { TickMath } from \"../src/util/TickMath\";\n\ndescribe(\"TickMath\", () => {\n  describe(\"#MIN_TICK\", () => {\n    it(\"eqls correct value\", () => {\n      expect(TickMath.MIN_TICK).eql(-887272);\n    });\n  });\n\n  describe(\"#MAX_TICK\", () => {\n    it(\"eqls correct value\", () => {\n      expect(TickMath.MAX_TICK).eql(887272);\n    });\n  });\n\n  describe(\"#getSqrtRatioAtTick\", () => {\n    it(\"throws for non integer\", () => {\n      expect(() => TickMath.getSqrtRatioAtTick(1.5)).throw(\"TICK\");\n    });\n\n    it(\"throws for tick too small\", () => {\n      expect(() => TickMath.getSqrtRatioAtTick(TickMath.MIN_TICK - 1)).throw(\n        \"TICK\"\n      );\n    });\n\n    it(\"throws for tick too large\", () => {\n      expect(() => TickMath.getSqrtRatioAtTick(TickMath.MAX_TICK + 1)).throw(\n        \"TICK\"\n      );\n    });\n\n    it(\"returns the correct value for min tick\", () => {\n      expect(TickMath.getSqrtRatioAtTick(TickMath.MIN_TICK)).eql(\n        TickMath.MIN_SQRT_RATIO\n      );\n    });\n\n    it(\"returns the correct value for tick 0\", () => {\n      expect(TickMath.getSqrtRatioAtTick(0)).eql(\n        JSBI.leftShift(JSBI.BigInt(1), JSBI.BigInt(96))\n      );\n    });\n\n    it(\"returns the correct value for max tick\", () => {\n      expect(TickMath.getSqrtRatioAtTick(TickMath.MAX_TICK)).eql(\n        TickMath.MAX_SQRT_RATIO\n      );\n    });\n  });\n\n  describe(\"#getTickAtSqrtRatio\", () => {\n    it(\"returns the correct value for sqrt ratio at min tick\", () => {\n      expect(TickMath.getTickAtSqrtRatio(TickMath.MIN_SQRT_RATIO)).eql(\n        TickMath.MIN_TICK\n      );\n    });\n    it(\"returns the correct value for sqrt ratio at max tick\", () => {\n      expect(\n        TickMath.getTickAtSqrtRatio(JSBI.subtract(TickMath.MAX_SQRT_RATIO, ONE))\n      ).eql(TickMath.MAX_TICK - 1);\n    });\n  });\n\n  describe(\"mostSignificantBit\", () => {\n    it(\"throws for zero\", () => {\n      expect(() => TickMath.mostSignificantBit(JSBI.BigInt(0))).throw(\"ZERO\");\n    });\n    it(\"correct value for every power of 2\", () => {\n      for (let i = 1; i < 256; i++) {\n        const x = JSBI.exponentiate(TWO, JSBI.BigInt(i));\n        expect(TickMath.mostSignificantBit(x)).equal(i);\n      }\n    });\n    it(\"correct value for every power of 2 - 1\", () => {\n      for (let i = 2; i < 256; i++) {\n        const x = JSBI.subtract(\n          JSBI.exponentiate(TWO, JSBI.BigInt(i)),\n          JSBI.BigInt(1)\n        );\n        expect(TickMath.mostSignificantBit(x)).equal(i - 1);\n      }\n    });\n\n    it(\"succeeds for MaxUint256\", () => {\n      expect(TickMath.mostSignificantBit(MaxUint256)).equal(255);\n    });\n\n    it(\"throws for MaxUint256 + 1\", () => {\n      expect(() =>\n        TickMath.mostSignificantBit(JSBI.add(MaxUint256, ONE))\n      ).throw(\"MAX\");\n    });\n  });\n});\n"
  },
  {
    "path": "test/UniswapV3Pool.spec.ts.pending",
    "content": "import { ethers, waffle } from \"hardhat\";\nimport { BigNumber, BigNumberish, constants, Wallet } from \"ethers\";\nimport { TestERC20 } from \"./stubs/TestERC20\";\n// import { UniswapV3Factory } from \"./stubs/UniswapV3Factory\";\nimport { MockTimeUniswapV3Pool } from \"./stubs/MockTimeUniswapV3Pool\";\n// import { TestUniswapV3SwapPay } from \"./stubs/TestUniswapV3SwapPay\";\n// import checkObservationEquals from \"./shared/checkObservationEquals\";\nimport { expect } from \"./shared/expect\";\n\n// import { poolFixture, TEST_POOL_START_TIME } from \"./shared/fixtures\";\n\nimport {\n  expandTo18Decimals,\n  FeeAmount,\n  getPositionKey,\n  getMaxTick,\n  getMinTick,\n  encodePriceSqrt,\n  TICK_SPACINGS,\n  createPoolFunctions,\n  SwapFunction,\n  MintFunction,\n  getMaxLiquidityPerTick,\n  FlashFunction,\n  MaxUint128,\n  MAX_SQRT_RATIO,\n  MIN_SQRT_RATIO,\n  SwapToPriceFunction,\n} from \"./shared/utilities\";\n// import { TestUniswapV3Callee } from \"./stubs/TestUniswapV3Callee\";\n// import { TestUniswapV3ReentrantCallee } from \"./stubs/TestUniswapV3ReentrantCallee\";\n// import { TickMathTest } from \"./stubs/TickMathTest\";\n// import { SwapMathTest } from \"./stubs/SwapMathTest\";\n\nconst createFixtureLoader = waffle.createFixtureLoader;\n\ntype ThenArg<T> = T extends PromiseLike<infer U> ? U : T;\n\ndescribe(\"UniswapV3Pool\", () => {\n  let wallet: Wallet, other: Wallet;\n\n  let token0: TestERC20;\n  let token1: TestERC20;\n  let token2: TestERC20;\n\n  let factory: UniswapV3Factory;\n  let pool: MockTimeUniswapV3Pool;\n\n  let swapTarget: TestUniswapV3Callee;\n\n  let swapToLowerPrice: SwapToPriceFunction;\n  let swapToHigherPrice: SwapToPriceFunction;\n  let swapExact0For1: SwapFunction;\n  let swap0ForExact1: SwapFunction;\n  let swapExact1For0: SwapFunction;\n  let swap1ForExact0: SwapFunction;\n\n  let feeAmount: number;\n  let tickSpacing: number;\n\n  let minTick: number;\n  let maxTick: number;\n\n  let mint: MintFunction;\n  let flash: FlashFunction;\n\n  let loadFixture: ReturnType<typeof createFixtureLoader>;\n  let createPool: ThenArg<ReturnType<typeof poolFixture>>[\"createPool\"];\n\n  before(\"create fixture loader\", async () => {\n    [wallet, other] = await (ethers as any).getSigners();\n    loadFixture = createFixtureLoader([wallet, other]);\n  });\n\n  beforeEach(\"deploy fixture\", async () => {\n    ({\n      token0,\n      token1,\n      token2,\n      factory,\n      createPool,\n      swapTargetCallee: swapTarget,\n    } = await loadFixture(poolFixture));\n\n    const oldCreatePool = createPool;\n    createPool = async (_feeAmount, _tickSpacing) => {\n      const pool = await oldCreatePool(_feeAmount, _tickSpacing);\n      ({\n        swapToLowerPrice,\n        swapToHigherPrice,\n        swapExact0For1,\n        swap0ForExact1,\n        swapExact1For0,\n        swap1ForExact0,\n        mint,\n        flash,\n      } = createPoolFunctions({\n        token0,\n        token1,\n        swapTarget,\n        pool,\n      }));\n      minTick = getMinTick(_tickSpacing);\n      maxTick = getMaxTick(_tickSpacing);\n      feeAmount = _feeAmount;\n      tickSpacing = _tickSpacing;\n      return pool;\n    };\n\n    // default to the 30 bips pool\n    pool = await createPool(FeeAmount.MEDIUM, TICK_SPACINGS[FeeAmount.MEDIUM]);\n  });\n\n  it(\"constructor initializes immutables\", async () => {\n    expect(await pool.factory()).to.eq(factory.address);\n    expect(await pool.token0()).to.eq(token0.address);\n    expect(await pool.token1()).to.eq(token1.address);\n    expect(await pool.maxLiquidityPerTick()).to.eq(\n      getMaxLiquidityPerTick(tickSpacing)\n    );\n  });\n\n  describe(\"#initialize\", () => {\n    it(\"fails if already initialized\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await expect(pool.initialize(encodePriceSqrt(1, 1))).to.be.reverted;\n    });\n    it(\"fails if starting price is too low\", async () => {\n      await expect(pool.initialize(1)).to.be.revertedWith(\"R\");\n      await expect(pool.initialize(MIN_SQRT_RATIO.sub(1))).to.be.revertedWith(\n        \"R\"\n      );\n    });\n    it(\"fails if starting price is too high\", async () => {\n      await expect(pool.initialize(MAX_SQRT_RATIO)).to.be.revertedWith(\"R\");\n      await expect(\n        pool.initialize(BigNumber.from(2).pow(160).sub(1))\n      ).to.be.revertedWith(\"R\");\n    });\n    it(\"can be initialized at MIN_SQRT_RATIO\", async () => {\n      await pool.initialize(MIN_SQRT_RATIO);\n      expect((await pool.slot0()).tick).to.eq(getMinTick(1));\n    });\n    it(\"can be initialized at MAX_SQRT_RATIO - 1\", async () => {\n      await pool.initialize(MAX_SQRT_RATIO.sub(1));\n      expect((await pool.slot0()).tick).to.eq(getMaxTick(1) - 1);\n    });\n    it(\"sets initial variables\", async () => {\n      const price = encodePriceSqrt(1, 2);\n      await pool.initialize(price);\n\n      const { sqrtPriceX96, observationIndex } = await pool.slot0();\n      expect(sqrtPriceX96).to.eq(price);\n      expect(observationIndex).to.eq(0);\n      expect((await pool.slot0()).tick).to.eq(-6932);\n    });\n    it(\"initializes the first observations slot\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n      checkObservationEquals(await pool.observations(0), {\n        secondsPerLiquidityCumulativeX128: 0,\n        initialized: true,\n        blockTimestamp: TEST_POOL_START_TIME,\n        tickCumulative: 0,\n      });\n    });\n    it(\"emits a Initialized event with the input tick\", async () => {\n      const sqrtPriceX96 = encodePriceSqrt(1, 2);\n      await expect(pool.initialize(sqrtPriceX96))\n        .to.emit(pool, \"Initialize\")\n        .withArgs(sqrtPriceX96, -6932);\n    });\n  });\n\n  describe(\"#mint\", () => {\n    it(\"fails if not initialized\", async () => {\n      await expect(\n        mint(wallet.address, -tickSpacing, tickSpacing, 1)\n      ).to.be.revertedWith(\"LOK\");\n    });\n    describe(\"after initialization\", () => {\n      beforeEach(\"initialize the pool at price of 10:1\", async () => {\n        await pool.initialize(encodePriceSqrt(1, 10));\n        await mint(wallet.address, minTick, maxTick, 3161);\n      });\n\n      describe(\"failure cases\", () => {\n        it(\"fails if tickLower greater than tickUpper\", async () => {\n          // should be TLU but...hardhat\n          await expect(mint(wallet.address, 1, 0, 1)).to.be.reverted;\n        });\n        it(\"fails if tickLower less than min tick\", async () => {\n          // should be TLM but...hardhat\n          await expect(mint(wallet.address, -887273, 0, 1)).to.be.reverted;\n        });\n        it(\"fails if tickUpper greater than max tick\", async () => {\n          // should be TUM but...hardhat\n          await expect(mint(wallet.address, 0, 887273, 1)).to.be.reverted;\n        });\n        it(\"fails if amount exceeds the max\", async () => {\n          // these should fail with 'LO' but hardhat is bugged\n          const maxLiquidityGross = await pool.maxLiquidityPerTick();\n          await expect(\n            mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              maxLiquidityGross.add(1)\n            )\n          ).to.be.reverted;\n          await expect(\n            mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              maxLiquidityGross\n            )\n          ).to.not.be.reverted;\n        });\n        it(\"fails if total amount at tick exceeds the max\", async () => {\n          // these should fail with 'LO' but hardhat is bugged\n          await mint(\n            wallet.address,\n            minTick + tickSpacing,\n            maxTick - tickSpacing,\n            1000\n          );\n\n          const maxLiquidityGross = await pool.maxLiquidityPerTick();\n          await expect(\n            mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              maxLiquidityGross.sub(1000).add(1)\n            )\n          ).to.be.reverted;\n          await expect(\n            mint(\n              wallet.address,\n              minTick + tickSpacing * 2,\n              maxTick - tickSpacing,\n              maxLiquidityGross.sub(1000).add(1)\n            )\n          ).to.be.reverted;\n          await expect(\n            mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing * 2,\n              maxLiquidityGross.sub(1000).add(1)\n            )\n          ).to.be.reverted;\n          await expect(\n            mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              maxLiquidityGross.sub(1000)\n            )\n          ).to.not.be.reverted;\n        });\n        it(\"fails if amount is 0\", async () => {\n          await expect(\n            mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              0\n            )\n          ).to.be.reverted;\n        });\n      });\n\n      describe(\"success cases\", () => {\n        it(\"initial balances\", async () => {\n          expect(await token0.balanceOf(pool.address)).to.eq(9996);\n          expect(await token1.balanceOf(pool.address)).to.eq(1000);\n        });\n\n        it(\"initial tick\", async () => {\n          expect((await pool.slot0()).tick).to.eq(-23028);\n        });\n\n        describe(\"above current price\", () => {\n          it(\"transfers token0 only\", async () => {\n            await expect(mint(wallet.address, -22980, 0, 10000))\n              .to.emit(token0, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 21549)\n              .to.not.emit(token1, \"Transfer\");\n            expect(await token0.balanceOf(pool.address)).to.eq(9996 + 21549);\n            expect(await token1.balanceOf(pool.address)).to.eq(1000);\n          });\n\n          it(\"max tick with max leverage\", async () => {\n            await mint(\n              wallet.address,\n              maxTick - tickSpacing,\n              maxTick,\n              BigNumber.from(2).pow(102)\n            );\n            expect(await token0.balanceOf(pool.address)).to.eq(\n              9996 + 828011525\n            );\n            expect(await token1.balanceOf(pool.address)).to.eq(1000);\n          });\n\n          it(\"works for max tick\", async () => {\n            await expect(mint(wallet.address, -22980, maxTick, 10000))\n              .to.emit(token0, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 31549);\n            expect(await token0.balanceOf(pool.address)).to.eq(9996 + 31549);\n            expect(await token1.balanceOf(pool.address)).to.eq(1000);\n          });\n\n          it(\"removing works\", async () => {\n            await mint(wallet.address, -240, 0, 10000);\n            await pool.burn(-240, 0, 10000);\n            const { amount0, amount1 } = await pool.collectStatic(\n              wallet.address,\n              -240,\n              0,\n              MaxUint128,\n              MaxUint128\n            );\n            expect(amount0, \"amount0\").to.eq(120);\n            expect(amount1, \"amount1\").to.eq(0);\n          });\n\n          it(\"adds liquidity to liquidityGross\", async () => {\n            await mint(wallet.address, -240, 0, 100);\n            expect((await pool.ticks(-240)).liquidityGross).to.eq(100);\n            expect((await pool.ticks(0)).liquidityGross).to.eq(100);\n            expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(0);\n            expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(0);\n            await mint(wallet.address, -240, tickSpacing, 150);\n            expect((await pool.ticks(-240)).liquidityGross).to.eq(250);\n            expect((await pool.ticks(0)).liquidityGross).to.eq(100);\n            expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(150);\n            expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(0);\n            await mint(wallet.address, 0, tickSpacing * 2, 60);\n            expect((await pool.ticks(-240)).liquidityGross).to.eq(250);\n            expect((await pool.ticks(0)).liquidityGross).to.eq(160);\n            expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(150);\n            expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(\n              60\n            );\n          });\n\n          it(\"removes liquidity from liquidityGross\", async () => {\n            await mint(wallet.address, -240, 0, 100);\n            await mint(wallet.address, -240, 0, 40);\n            await pool.burn(-240, 0, 90);\n            expect((await pool.ticks(-240)).liquidityGross).to.eq(50);\n            expect((await pool.ticks(0)).liquidityGross).to.eq(50);\n          });\n\n          it(\"clears tick lower if last position is removed\", async () => {\n            await mint(wallet.address, -240, 0, 100);\n            await pool.burn(-240, 0, 100);\n            const {\n              liquidityGross,\n              feeGrowthOutside0X128,\n              feeGrowthOutside1X128,\n            } = await pool.ticks(-240);\n            expect(liquidityGross).to.eq(0);\n            expect(feeGrowthOutside0X128).to.eq(0);\n            expect(feeGrowthOutside1X128).to.eq(0);\n          });\n\n          it(\"clears tick upper if last position is removed\", async () => {\n            await mint(wallet.address, -240, 0, 100);\n            await pool.burn(-240, 0, 100);\n            const {\n              liquidityGross,\n              feeGrowthOutside0X128,\n              feeGrowthOutside1X128,\n            } = await pool.ticks(0);\n            expect(liquidityGross).to.eq(0);\n            expect(feeGrowthOutside0X128).to.eq(0);\n            expect(feeGrowthOutside1X128).to.eq(0);\n          });\n          it(\"only clears the tick that is not used at all\", async () => {\n            await mint(wallet.address, -240, 0, 100);\n            await mint(wallet.address, -tickSpacing, 0, 250);\n            await pool.burn(-240, 0, 100);\n\n            let {\n              liquidityGross,\n              feeGrowthOutside0X128,\n              feeGrowthOutside1X128,\n            } = await pool.ticks(-240);\n            expect(liquidityGross).to.eq(0);\n            expect(feeGrowthOutside0X128).to.eq(0);\n            expect(feeGrowthOutside1X128).to.eq(0);\n            ({ liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } =\n              await pool.ticks(-tickSpacing));\n            expect(liquidityGross).to.eq(250);\n            expect(feeGrowthOutside0X128).to.eq(0);\n            expect(feeGrowthOutside1X128).to.eq(0);\n          });\n\n          it(\"does not write an observation\", async () => {\n            checkObservationEquals(await pool.observations(0), {\n              tickCumulative: 0,\n              blockTimestamp: TEST_POOL_START_TIME,\n              initialized: true,\n              secondsPerLiquidityCumulativeX128: 0,\n            });\n            await pool.advanceTime(1);\n            await mint(wallet.address, -240, 0, 100);\n            checkObservationEquals(await pool.observations(0), {\n              tickCumulative: 0,\n              blockTimestamp: TEST_POOL_START_TIME,\n              initialized: true,\n              secondsPerLiquidityCumulativeX128: 0,\n            });\n          });\n        });\n\n        describe(\"including current price\", () => {\n          it(\"price within range: transfers current price of both tokens\", async () => {\n            await expect(\n              mint(\n                wallet.address,\n                minTick + tickSpacing,\n                maxTick - tickSpacing,\n                100\n              )\n            )\n              .to.emit(token0, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 317)\n              .to.emit(token1, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 32);\n            expect(await token0.balanceOf(pool.address)).to.eq(9996 + 317);\n            expect(await token1.balanceOf(pool.address)).to.eq(1000 + 32);\n          });\n\n          it(\"initializes lower tick\", async () => {\n            await mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              100\n            );\n            const { liquidityGross } = await pool.ticks(minTick + tickSpacing);\n            expect(liquidityGross).to.eq(100);\n          });\n\n          it(\"initializes upper tick\", async () => {\n            await mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              100\n            );\n            const { liquidityGross } = await pool.ticks(maxTick - tickSpacing);\n            expect(liquidityGross).to.eq(100);\n          });\n\n          it(\"works for min/max tick\", async () => {\n            await expect(mint(wallet.address, minTick, maxTick, 10000))\n              .to.emit(token0, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 31623)\n              .to.emit(token1, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 3163);\n            expect(await token0.balanceOf(pool.address)).to.eq(9996 + 31623);\n            expect(await token1.balanceOf(pool.address)).to.eq(1000 + 3163);\n          });\n\n          it(\"removing works\", async () => {\n            await mint(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              100\n            );\n            await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 100);\n            const { amount0, amount1 } = await pool.collectStatic(\n              wallet.address,\n              minTick + tickSpacing,\n              maxTick - tickSpacing,\n              MaxUint128,\n              MaxUint128\n            );\n            expect(amount0, \"amount0\").to.eq(316);\n            expect(amount1, \"amount1\").to.eq(31);\n          });\n\n          it(\"writes an observation\", async () => {\n            checkObservationEquals(await pool.observations(0), {\n              tickCumulative: 0,\n              blockTimestamp: TEST_POOL_START_TIME,\n              initialized: true,\n              secondsPerLiquidityCumulativeX128: 0,\n            });\n            await pool.advanceTime(1);\n            await mint(wallet.address, minTick, maxTick, 100);\n            checkObservationEquals(await pool.observations(0), {\n              tickCumulative: -23028,\n              blockTimestamp: TEST_POOL_START_TIME + 1,\n              initialized: true,\n              secondsPerLiquidityCumulativeX128:\n                \"107650226801941937191829992860413859\",\n            });\n          });\n        });\n\n        describe(\"below current price\", () => {\n          it(\"transfers token1 only\", async () => {\n            await expect(mint(wallet.address, -46080, -23040, 10000))\n              .to.emit(token1, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 2162)\n              .to.not.emit(token0, \"Transfer\");\n            expect(await token0.balanceOf(pool.address)).to.eq(9996);\n            expect(await token1.balanceOf(pool.address)).to.eq(1000 + 2162);\n          });\n\n          it(\"min tick with max leverage\", async () => {\n            await mint(\n              wallet.address,\n              minTick,\n              minTick + tickSpacing,\n              BigNumber.from(2).pow(102)\n            );\n            expect(await token0.balanceOf(pool.address)).to.eq(9996);\n            expect(await token1.balanceOf(pool.address)).to.eq(\n              1000 + 828011520\n            );\n          });\n\n          it(\"works for min tick\", async () => {\n            await expect(mint(wallet.address, minTick, -23040, 10000))\n              .to.emit(token1, \"Transfer\")\n              .withArgs(wallet.address, pool.address, 3161);\n            expect(await token0.balanceOf(pool.address)).to.eq(9996);\n            expect(await token1.balanceOf(pool.address)).to.eq(1000 + 3161);\n          });\n\n          it(\"removing works\", async () => {\n            await mint(wallet.address, -46080, -46020, 10000);\n            await pool.burn(-46080, -46020, 10000);\n            const { amount0, amount1 } = await pool.collectStatic(\n              wallet.address,\n              -46080,\n              -46020,\n              MaxUint128,\n              MaxUint128\n            );\n            expect(amount0, \"amount0\").to.eq(0);\n            expect(amount1, \"amount1\").to.eq(3);\n          });\n\n          it(\"does not write an observation\", async () => {\n            checkObservationEquals(await pool.observations(0), {\n              tickCumulative: 0,\n              blockTimestamp: TEST_POOL_START_TIME,\n              initialized: true,\n              secondsPerLiquidityCumulativeX128: 0,\n            });\n            await pool.advanceTime(1);\n            await mint(wallet.address, -46080, -23040, 100);\n            checkObservationEquals(await pool.observations(0), {\n              tickCumulative: 0,\n              blockTimestamp: TEST_POOL_START_TIME,\n              initialized: true,\n              secondsPerLiquidityCumulativeX128: 0,\n            });\n          });\n        });\n      });\n\n      it(\"protocol fees accumulate as expected during swap\", async () => {\n        await pool.setFeeProtocol(6, 6);\n\n        await mint(\n          wallet.address,\n          minTick + tickSpacing,\n          maxTick - tickSpacing,\n          expandTo18Decimals(1)\n        );\n        await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address);\n        await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address);\n\n        let { token0: token0ProtocolFees, token1: token1ProtocolFees } =\n          await pool.protocolFees();\n        expect(token0ProtocolFees).to.eq(\"50000000000000\");\n        expect(token1ProtocolFees).to.eq(\"5000000000000\");\n      });\n\n      it(\"positions are protected before protocol fee is turned on\", async () => {\n        await mint(\n          wallet.address,\n          minTick + tickSpacing,\n          maxTick - tickSpacing,\n          expandTo18Decimals(1)\n        );\n        await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address);\n        await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address);\n\n        let { token0: token0ProtocolFees, token1: token1ProtocolFees } =\n          await pool.protocolFees();\n        expect(token0ProtocolFees).to.eq(0);\n        expect(token1ProtocolFees).to.eq(0);\n\n        await pool.setFeeProtocol(6, 6);\n        ({ token0: token0ProtocolFees, token1: token1ProtocolFees } =\n          await pool.protocolFees());\n        expect(token0ProtocolFees).to.eq(0);\n        expect(token1ProtocolFees).to.eq(0);\n      });\n\n      it(\"poke is not allowed on uninitialized position\", async () => {\n        await mint(\n          other.address,\n          minTick + tickSpacing,\n          maxTick - tickSpacing,\n          expandTo18Decimals(1)\n        );\n        await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address);\n        await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address);\n\n        // missing revert reason due to hardhat\n        await expect(pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 0))\n          .to.be.reverted;\n\n        await mint(\n          wallet.address,\n          minTick + tickSpacing,\n          maxTick - tickSpacing,\n          1\n        );\n        let {\n          liquidity,\n          feeGrowthInside0LastX128,\n          feeGrowthInside1LastX128,\n          tokensOwed1,\n          tokensOwed0,\n        } = await pool.positions(\n          getPositionKey(\n            wallet.address,\n            minTick + tickSpacing,\n            maxTick - tickSpacing\n          )\n        );\n        expect(liquidity).to.eq(1);\n        expect(feeGrowthInside0LastX128).to.eq(\n          \"102084710076281216349243831104605583\"\n        );\n        expect(feeGrowthInside1LastX128).to.eq(\n          \"10208471007628121634924383110460558\"\n        );\n        expect(tokensOwed0, \"tokens owed 0 before\").to.eq(0);\n        expect(tokensOwed1, \"tokens owed 1 before\").to.eq(0);\n\n        await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 1);\n        ({\n          liquidity,\n          feeGrowthInside0LastX128,\n          feeGrowthInside1LastX128,\n          tokensOwed1,\n          tokensOwed0,\n        } = await pool.positions(\n          getPositionKey(\n            wallet.address,\n            minTick + tickSpacing,\n            maxTick - tickSpacing\n          )\n        ));\n        expect(liquidity).to.eq(0);\n        expect(feeGrowthInside0LastX128).to.eq(\n          \"102084710076281216349243831104605583\"\n        );\n        expect(feeGrowthInside1LastX128).to.eq(\n          \"10208471007628121634924383110460558\"\n        );\n        expect(tokensOwed0, \"tokens owed 0 after\").to.eq(3);\n        expect(tokensOwed1, \"tokens owed 1 after\").to.eq(0);\n      });\n    });\n  });\n\n  describe(\"#burn\", () => {\n    beforeEach(\"initialize at zero tick\", () => initializeAtZeroTick(pool));\n\n    async function checkTickIsClear(tick: number) {\n      const {\n        liquidityGross,\n        feeGrowthOutside0X128,\n        feeGrowthOutside1X128,\n        liquidityNet,\n      } = await pool.ticks(tick);\n      expect(liquidityGross).to.eq(0);\n      expect(feeGrowthOutside0X128).to.eq(0);\n      expect(feeGrowthOutside1X128).to.eq(0);\n      expect(liquidityNet).to.eq(0);\n    }\n\n    async function checkTickIsNotClear(tick: number) {\n      const { liquidityGross } = await pool.ticks(tick);\n      expect(liquidityGross).to.not.eq(0);\n    }\n\n    it(\"does not clear the position fee growth snapshot if no more liquidity\", async () => {\n      // some activity that would make the ticks non-zero\n      await pool.advanceTime(10);\n      await mint(other.address, minTick, maxTick, expandTo18Decimals(1));\n      await swapExact0For1(expandTo18Decimals(1), wallet.address);\n      await swapExact1For0(expandTo18Decimals(1), wallet.address);\n      await pool.connect(other).burn(minTick, maxTick, expandTo18Decimals(1));\n      const {\n        liquidity,\n        tokensOwed0,\n        tokensOwed1,\n        feeGrowthInside0LastX128,\n        feeGrowthInside1LastX128,\n      } = await pool.positions(getPositionKey(other.address, minTick, maxTick));\n      expect(liquidity).to.eq(0);\n      expect(tokensOwed0).to.not.eq(0);\n      expect(tokensOwed1).to.not.eq(0);\n      expect(feeGrowthInside0LastX128).to.eq(\n        \"340282366920938463463374607431768211\"\n      );\n      expect(feeGrowthInside1LastX128).to.eq(\n        \"340282366920938576890830247744589365\"\n      );\n    });\n\n    it(\"clears the tick if its the last position using it\", async () => {\n      const tickLower = minTick + tickSpacing;\n      const tickUpper = maxTick - tickSpacing;\n      // some activity that would make the ticks non-zero\n      await pool.advanceTime(10);\n      await mint(wallet.address, tickLower, tickUpper, 1);\n      await swapExact0For1(expandTo18Decimals(1), wallet.address);\n      await pool.burn(tickLower, tickUpper, 1);\n      await checkTickIsClear(tickLower);\n      await checkTickIsClear(tickUpper);\n    });\n\n    it(\"clears only the lower tick if upper is still used\", async () => {\n      const tickLower = minTick + tickSpacing;\n      const tickUpper = maxTick - tickSpacing;\n      // some activity that would make the ticks non-zero\n      await pool.advanceTime(10);\n      await mint(wallet.address, tickLower, tickUpper, 1);\n      await mint(wallet.address, tickLower + tickSpacing, tickUpper, 1);\n      await swapExact0For1(expandTo18Decimals(1), wallet.address);\n      await pool.burn(tickLower, tickUpper, 1);\n      await checkTickIsClear(tickLower);\n      await checkTickIsNotClear(tickUpper);\n    });\n\n    it(\"clears only the upper tick if lower is still used\", async () => {\n      const tickLower = minTick + tickSpacing;\n      const tickUpper = maxTick - tickSpacing;\n      // some activity that would make the ticks non-zero\n      await pool.advanceTime(10);\n      await mint(wallet.address, tickLower, tickUpper, 1);\n      await mint(wallet.address, tickLower, tickUpper - tickSpacing, 1);\n      await swapExact0For1(expandTo18Decimals(1), wallet.address);\n      await pool.burn(tickLower, tickUpper, 1);\n      await checkTickIsNotClear(tickLower);\n      await checkTickIsClear(tickUpper);\n    });\n  });\n\n  // the combined amount of liquidity that the pool is initialized with (including the 1 minimum liquidity that is burned)\n  const initializeLiquidityAmount = expandTo18Decimals(2);\n  async function initializeAtZeroTick(\n    pool: MockTimeUniswapV3Pool\n  ): Promise<void> {\n    await pool.initialize(encodePriceSqrt(1, 1));\n    const tickSpacing = await pool.tickSpacing();\n    const [min, max] = [getMinTick(tickSpacing), getMaxTick(tickSpacing)];\n    await mint(wallet.address, min, max, initializeLiquidityAmount);\n  }\n\n  describe(\"#observe\", () => {\n    beforeEach(() => initializeAtZeroTick(pool));\n\n    // zero tick\n    it(\"current tick accumulator increases by tick over time\", async () => {\n      let {\n        tickCumulatives: [tickCumulative],\n      } = await pool.observe([0]);\n      expect(tickCumulative).to.eq(0);\n      await pool.advanceTime(10);\n      ({\n        tickCumulatives: [tickCumulative],\n      } = await pool.observe([0]));\n      expect(tickCumulative).to.eq(0);\n    });\n\n    it(\"current tick accumulator after single swap\", async () => {\n      // moves to tick -1\n      await swapExact0For1(1000, wallet.address);\n      await pool.advanceTime(4);\n      let {\n        tickCumulatives: [tickCumulative],\n      } = await pool.observe([0]);\n      expect(tickCumulative).to.eq(-4);\n    });\n\n    it(\"current tick accumulator after two swaps\", async () => {\n      await swapExact0For1(expandTo18Decimals(1).div(2), wallet.address);\n      expect((await pool.slot0()).tick).to.eq(-4452);\n      await pool.advanceTime(4);\n      await swapExact1For0(expandTo18Decimals(1).div(4), wallet.address);\n      expect((await pool.slot0()).tick).to.eq(-1558);\n      await pool.advanceTime(6);\n      let {\n        tickCumulatives: [tickCumulative],\n      } = await pool.observe([0]);\n      // -4452*4 + -1558*6\n      expect(tickCumulative).to.eq(-27156);\n    });\n  });\n\n  describe(\"miscellaneous mint tests\", () => {\n    beforeEach(\"initialize at zero tick\", async () => {\n      pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]);\n      await initializeAtZeroTick(pool);\n    });\n\n    it(\"mint to the right of the current price\", async () => {\n      const liquidityDelta = 1000;\n      const lowerTick = tickSpacing;\n      const upperTick = tickSpacing * 2;\n\n      const liquidityBefore = await pool.liquidity();\n\n      const b0 = await token0.balanceOf(pool.address);\n      const b1 = await token1.balanceOf(pool.address);\n\n      await mint(wallet.address, lowerTick, upperTick, liquidityDelta);\n\n      const liquidityAfter = await pool.liquidity();\n      expect(liquidityAfter).to.be.gte(liquidityBefore);\n\n      expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(1);\n      expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(0);\n    });\n\n    it(\"mint to the left of the current price\", async () => {\n      const liquidityDelta = 1000;\n      const lowerTick = -tickSpacing * 2;\n      const upperTick = -tickSpacing;\n\n      const liquidityBefore = await pool.liquidity();\n\n      const b0 = await token0.balanceOf(pool.address);\n      const b1 = await token1.balanceOf(pool.address);\n\n      await mint(wallet.address, lowerTick, upperTick, liquidityDelta);\n\n      const liquidityAfter = await pool.liquidity();\n      expect(liquidityAfter).to.be.gte(liquidityBefore);\n\n      expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(0);\n      expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(1);\n    });\n\n    it(\"mint within the current price\", async () => {\n      const liquidityDelta = 1000;\n      const lowerTick = -tickSpacing;\n      const upperTick = tickSpacing;\n\n      const liquidityBefore = await pool.liquidity();\n\n      const b0 = await token0.balanceOf(pool.address);\n      const b1 = await token1.balanceOf(pool.address);\n\n      await mint(wallet.address, lowerTick, upperTick, liquidityDelta);\n\n      const liquidityAfter = await pool.liquidity();\n      expect(liquidityAfter).to.be.gte(liquidityBefore);\n\n      expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(1);\n      expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(1);\n    });\n\n    it(\"cannot remove more than the entire position\", async () => {\n      const lowerTick = -tickSpacing;\n      const upperTick = tickSpacing;\n      await mint(\n        wallet.address,\n        lowerTick,\n        upperTick,\n        expandTo18Decimals(1000)\n      );\n      // should be 'LS', hardhat is bugged\n      await expect(pool.burn(lowerTick, upperTick, expandTo18Decimals(1001))).to\n        .be.reverted;\n    });\n\n    it(\"collect fees within the current price after swap\", async () => {\n      const liquidityDelta = expandTo18Decimals(100);\n      const lowerTick = -tickSpacing * 100;\n      const upperTick = tickSpacing * 100;\n\n      await mint(wallet.address, lowerTick, upperTick, liquidityDelta);\n\n      const liquidityBefore = await pool.liquidity();\n\n      const amount0In = expandTo18Decimals(1);\n      await swapExact0For1(amount0In, wallet.address);\n\n      const liquidityAfter = await pool.liquidity();\n      expect(liquidityAfter, \"k increases\").to.be.gte(liquidityBefore);\n\n      const token0BalanceBeforePool = await token0.balanceOf(pool.address);\n      const token1BalanceBeforePool = await token1.balanceOf(pool.address);\n      const token0BalanceBeforeWallet = await token0.balanceOf(wallet.address);\n      const token1BalanceBeforeWallet = await token1.balanceOf(wallet.address);\n\n      await pool.burn(lowerTick, upperTick, 0);\n      await pool.collect(\n        wallet.address,\n        lowerTick,\n        upperTick,\n        MaxUint128,\n        MaxUint128\n      );\n\n      await pool.burn(lowerTick, upperTick, 0);\n      const { amount0: fees0, amount1: fees1 } = await pool.collectStatic(\n        wallet.address,\n        lowerTick,\n        upperTick,\n        MaxUint128,\n        MaxUint128\n      );\n      expect(fees0).to.be.eq(0);\n      expect(fees1).to.be.eq(0);\n\n      const token0BalanceAfterWallet = await token0.balanceOf(wallet.address);\n      const token1BalanceAfterWallet = await token1.balanceOf(wallet.address);\n      const token0BalanceAfterPool = await token0.balanceOf(pool.address);\n      const token1BalanceAfterPool = await token1.balanceOf(pool.address);\n\n      expect(token0BalanceAfterWallet).to.be.gt(token0BalanceBeforeWallet);\n      expect(token1BalanceAfterWallet).to.be.eq(token1BalanceBeforeWallet);\n\n      expect(token0BalanceAfterPool).to.be.lt(token0BalanceBeforePool);\n      expect(token1BalanceAfterPool).to.be.eq(token1BalanceBeforePool);\n    });\n  });\n\n  describe(\"post-initialize at medium fee\", () => {\n    describe(\"k (implicit)\", () => {\n      it(\"returns 0 before initialization\", async () => {\n        expect(await pool.liquidity()).to.eq(0);\n      });\n      describe(\"post initialized\", () => {\n        beforeEach(() => initializeAtZeroTick(pool));\n\n        it(\"returns initial liquidity\", async () => {\n          expect(await pool.liquidity()).to.eq(expandTo18Decimals(2));\n        });\n        it(\"returns in supply in range\", async () => {\n          await mint(\n            wallet.address,\n            -tickSpacing,\n            tickSpacing,\n            expandTo18Decimals(3)\n          );\n          expect(await pool.liquidity()).to.eq(expandTo18Decimals(5));\n        });\n        it(\"excludes supply at tick above current tick\", async () => {\n          await mint(\n            wallet.address,\n            tickSpacing,\n            tickSpacing * 2,\n            expandTo18Decimals(3)\n          );\n          expect(await pool.liquidity()).to.eq(expandTo18Decimals(2));\n        });\n        it(\"excludes supply at tick below current tick\", async () => {\n          await mint(\n            wallet.address,\n            -tickSpacing * 2,\n            -tickSpacing,\n            expandTo18Decimals(3)\n          );\n          expect(await pool.liquidity()).to.eq(expandTo18Decimals(2));\n        });\n        it(\"updates correctly when exiting range\", async () => {\n          const kBefore = await pool.liquidity();\n          expect(kBefore).to.be.eq(expandTo18Decimals(2));\n\n          // add liquidity at and above current tick\n          const liquidityDelta = expandTo18Decimals(1);\n          const lowerTick = 0;\n          const upperTick = tickSpacing;\n          await mint(wallet.address, lowerTick, upperTick, liquidityDelta);\n\n          // ensure virtual supply has increased appropriately\n          const kAfter = await pool.liquidity();\n          expect(kAfter).to.be.eq(expandTo18Decimals(3));\n\n          // swap toward the left (just enough for the tick transition function to trigger)\n          await swapExact0For1(1, wallet.address);\n          const { tick } = await pool.slot0();\n          expect(tick).to.be.eq(-1);\n\n          const kAfterSwap = await pool.liquidity();\n          expect(kAfterSwap).to.be.eq(expandTo18Decimals(2));\n        });\n        it(\"updates correctly when entering range\", async () => {\n          const kBefore = await pool.liquidity();\n          expect(kBefore).to.be.eq(expandTo18Decimals(2));\n\n          // add liquidity below the current tick\n          const liquidityDelta = expandTo18Decimals(1);\n          const lowerTick = -tickSpacing;\n          const upperTick = 0;\n          await mint(wallet.address, lowerTick, upperTick, liquidityDelta);\n\n          // ensure virtual supply hasn't changed\n          const kAfter = await pool.liquidity();\n          expect(kAfter).to.be.eq(kBefore);\n\n          // swap toward the left (just enough for the tick transition function to trigger)\n          await swapExact0For1(1, wallet.address);\n          const { tick } = await pool.slot0();\n          expect(tick).to.be.eq(-1);\n\n          const kAfterSwap = await pool.liquidity();\n          expect(kAfterSwap).to.be.eq(expandTo18Decimals(3));\n        });\n      });\n    });\n  });\n\n  describe(\"limit orders\", () => {\n    beforeEach(\"initialize at tick 0\", () => initializeAtZeroTick(pool));\n\n    it(\"limit selling 0 for 1 at tick 0 thru 1\", async () => {\n      await expect(mint(wallet.address, 0, 120, expandTo18Decimals(1)))\n        .to.emit(token0, \"Transfer\")\n        .withArgs(wallet.address, pool.address, \"5981737760509663\");\n      // somebody takes the limit order\n      await swapExact1For0(expandTo18Decimals(2), other.address);\n      await expect(pool.burn(0, 120, expandTo18Decimals(1)))\n        .to.emit(pool, \"Burn\")\n        .withArgs(\n          wallet.address,\n          0,\n          120,\n          expandTo18Decimals(1),\n          0,\n          \"6017734268818165\"\n        )\n        .to.not.emit(token0, \"Transfer\")\n        .to.not.emit(token1, \"Transfer\");\n      await expect(pool.collect(wallet.address, 0, 120, MaxUint128, MaxUint128))\n        .to.emit(token1, \"Transfer\")\n        .withArgs(\n          pool.address,\n          wallet.address,\n          BigNumber.from(\"6017734268818165\").add(\"18107525382602\")\n        ) // roughly 0.3% despite other liquidity\n        .to.not.emit(token0, \"Transfer\");\n      expect((await pool.slot0()).tick).to.be.gte(120);\n    });\n    it(\"limit selling 1 for 0 at tick 0 thru -1\", async () => {\n      await expect(mint(wallet.address, -120, 0, expandTo18Decimals(1)))\n        .to.emit(token1, \"Transfer\")\n        .withArgs(wallet.address, pool.address, \"5981737760509663\");\n      // somebody takes the limit order\n      await swapExact0For1(expandTo18Decimals(2), other.address);\n      await expect(pool.burn(-120, 0, expandTo18Decimals(1)))\n        .to.emit(pool, \"Burn\")\n        .withArgs(\n          wallet.address,\n          -120,\n          0,\n          expandTo18Decimals(1),\n          \"6017734268818165\",\n          0\n        )\n        .to.not.emit(token0, \"Transfer\")\n        .to.not.emit(token1, \"Transfer\");\n      await expect(\n        pool.collect(wallet.address, -120, 0, MaxUint128, MaxUint128)\n      )\n        .to.emit(token0, \"Transfer\")\n        .withArgs(\n          pool.address,\n          wallet.address,\n          BigNumber.from(\"6017734268818165\").add(\"18107525382602\")\n        ); // roughly 0.3% despite other liquidity\n      expect((await pool.slot0()).tick).to.be.lt(-120);\n    });\n\n    describe(\"fee is on\", () => {\n      beforeEach(() => pool.setFeeProtocol(6, 6));\n      it(\"limit selling 0 for 1 at tick 0 thru 1\", async () => {\n        await expect(mint(wallet.address, 0, 120, expandTo18Decimals(1)))\n          .to.emit(token0, \"Transfer\")\n          .withArgs(wallet.address, pool.address, \"5981737760509663\");\n        // somebody takes the limit order\n        await swapExact1For0(expandTo18Decimals(2), other.address);\n        await expect(pool.burn(0, 120, expandTo18Decimals(1)))\n          .to.emit(pool, \"Burn\")\n          .withArgs(\n            wallet.address,\n            0,\n            120,\n            expandTo18Decimals(1),\n            0,\n            \"6017734268818165\"\n          )\n          .to.not.emit(token0, \"Transfer\")\n          .to.not.emit(token1, \"Transfer\");\n        await expect(\n          pool.collect(wallet.address, 0, 120, MaxUint128, MaxUint128)\n        )\n          .to.emit(token1, \"Transfer\")\n          .withArgs(\n            pool.address,\n            wallet.address,\n            BigNumber.from(\"6017734268818165\").add(\"15089604485501\")\n          ) // roughly 0.25% despite other liquidity\n          .to.not.emit(token0, \"Transfer\");\n        expect((await pool.slot0()).tick).to.be.gte(120);\n      });\n      it(\"limit selling 1 for 0 at tick 0 thru -1\", async () => {\n        await expect(mint(wallet.address, -120, 0, expandTo18Decimals(1)))\n          .to.emit(token1, \"Transfer\")\n          .withArgs(wallet.address, pool.address, \"5981737760509663\");\n        // somebody takes the limit order\n        await swapExact0For1(expandTo18Decimals(2), other.address);\n        await expect(pool.burn(-120, 0, expandTo18Decimals(1)))\n          .to.emit(pool, \"Burn\")\n          .withArgs(\n            wallet.address,\n            -120,\n            0,\n            expandTo18Decimals(1),\n            \"6017734268818165\",\n            0\n          )\n          .to.not.emit(token0, \"Transfer\")\n          .to.not.emit(token1, \"Transfer\");\n        await expect(\n          pool.collect(wallet.address, -120, 0, MaxUint128, MaxUint128)\n        )\n          .to.emit(token0, \"Transfer\")\n          .withArgs(\n            pool.address,\n            wallet.address,\n            BigNumber.from(\"6017734268818165\").add(\"15089604485501\")\n          ); // roughly 0.25% despite other liquidity\n        expect((await pool.slot0()).tick).to.be.lt(-120);\n      });\n    });\n  });\n\n  describe(\"#collect\", () => {\n    beforeEach(async () => {\n      pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]);\n      await pool.initialize(encodePriceSqrt(1, 1));\n    });\n\n    it(\"works with multiple LPs\", async () => {\n      await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1));\n      await mint(\n        wallet.address,\n        minTick + tickSpacing,\n        maxTick - tickSpacing,\n        expandTo18Decimals(2)\n      );\n\n      await swapExact0For1(expandTo18Decimals(1), wallet.address);\n\n      // poke positions\n      await pool.burn(minTick, maxTick, 0);\n      await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 0);\n\n      const { tokensOwed0: tokensOwed0Position0 } = await pool.positions(\n        getPositionKey(wallet.address, minTick, maxTick)\n      );\n      const { tokensOwed0: tokensOwed0Position1 } = await pool.positions(\n        getPositionKey(\n          wallet.address,\n          minTick + tickSpacing,\n          maxTick - tickSpacing\n        )\n      );\n\n      expect(tokensOwed0Position0).to.be.eq(\"166666666666667\");\n      expect(tokensOwed0Position1).to.be.eq(\"333333333333334\");\n    });\n\n    describe(\"works across large increases\", () => {\n      beforeEach(async () => {\n        await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1));\n      });\n\n      // type(uint128).max * 2**128 / 1e18\n      // https://www.wolframalpha.com/input/?i=%282**128+-+1%29+*+2**128+%2F+1e18\n      const magicNumber = BigNumber.from(\n        \"115792089237316195423570985008687907852929702298719625575994\"\n      );\n\n      it(\"works just before the cap binds\", async () => {\n        await pool.setFeeGrowthGlobal0X128(magicNumber);\n        await pool.burn(minTick, maxTick, 0);\n\n        const { tokensOwed0, tokensOwed1 } = await pool.positions(\n          getPositionKey(wallet.address, minTick, maxTick)\n        );\n\n        expect(tokensOwed0).to.be.eq(MaxUint128.sub(1));\n        expect(tokensOwed1).to.be.eq(0);\n      });\n\n      it(\"works just after the cap binds\", async () => {\n        await pool.setFeeGrowthGlobal0X128(magicNumber.add(1));\n        await pool.burn(minTick, maxTick, 0);\n\n        const { tokensOwed0, tokensOwed1 } = await pool.positions(\n          getPositionKey(wallet.address, minTick, maxTick)\n        );\n\n        expect(tokensOwed0).to.be.eq(MaxUint128);\n        expect(tokensOwed1).to.be.eq(0);\n      });\n\n      it(\"works well after the cap binds\", async () => {\n        await pool.setFeeGrowthGlobal0X128(constants.MaxUint256);\n        await pool.burn(minTick, maxTick, 0);\n\n        const { tokensOwed0, tokensOwed1 } = await pool.positions(\n          getPositionKey(wallet.address, minTick, maxTick)\n        );\n\n        expect(tokensOwed0).to.be.eq(MaxUint128);\n        expect(tokensOwed1).to.be.eq(0);\n      });\n    });\n\n    describe(\"works across overflow boundaries\", () => {\n      beforeEach(async () => {\n        await pool.setFeeGrowthGlobal0X128(constants.MaxUint256);\n        await pool.setFeeGrowthGlobal1X128(constants.MaxUint256);\n        await mint(wallet.address, minTick, maxTick, expandTo18Decimals(10));\n      });\n\n      it(\"token0\", async () => {\n        await swapExact0For1(expandTo18Decimals(1), wallet.address);\n        await pool.burn(minTick, maxTick, 0);\n        const { amount0, amount1 } = await pool.collectStatic(\n          wallet.address,\n          minTick,\n          maxTick,\n          MaxUint128,\n          MaxUint128\n        );\n        expect(amount0).to.be.eq(\"499999999999999\");\n        expect(amount1).to.be.eq(0);\n      });\n      it(\"token1\", async () => {\n        await swapExact1For0(expandTo18Decimals(1), wallet.address);\n        await pool.burn(minTick, maxTick, 0);\n        const { amount0, amount1 } = await pool.collectStatic(\n          wallet.address,\n          minTick,\n          maxTick,\n          MaxUint128,\n          MaxUint128\n        );\n        expect(amount0).to.be.eq(0);\n        expect(amount1).to.be.eq(\"499999999999999\");\n      });\n      it(\"token0 and token1\", async () => {\n        await swapExact0For1(expandTo18Decimals(1), wallet.address);\n        await swapExact1For0(expandTo18Decimals(1), wallet.address);\n        await pool.burn(minTick, maxTick, 0);\n        const { amount0, amount1 } = await pool.collectStatic(\n          wallet.address,\n          minTick,\n          maxTick,\n          MaxUint128,\n          MaxUint128\n        );\n        expect(amount0).to.be.eq(\"499999999999999\");\n        expect(amount1).to.be.eq(\"500000000000000\");\n      });\n    });\n  });\n\n  describe(\"#feeProtocol\", () => {\n    const liquidityAmount = expandTo18Decimals(1000);\n\n    beforeEach(async () => {\n      pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]);\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await mint(wallet.address, minTick, maxTick, liquidityAmount);\n    });\n\n    it(\"is initially set to 0\", async () => {\n      expect((await pool.slot0()).feeProtocol).to.eq(0);\n    });\n\n    it(\"can be changed by the owner\", async () => {\n      await pool.setFeeProtocol(6, 6);\n      expect((await pool.slot0()).feeProtocol).to.eq(102);\n    });\n\n    it(\"cannot be changed out of bounds\", async () => {\n      await expect(pool.setFeeProtocol(3, 3)).to.be.reverted;\n      await expect(pool.setFeeProtocol(11, 11)).to.be.reverted;\n    });\n\n    it(\"cannot be changed by addresses that are not owner\", async () => {\n      await expect(pool.connect(other).setFeeProtocol(6, 6)).to.be.reverted;\n    });\n\n    async function swapAndGetFeesOwed({\n      amount,\n      zeroForOne,\n      poke,\n    }: {\n      amount: BigNumberish;\n      zeroForOne: boolean;\n      poke: boolean;\n    }) {\n      await (zeroForOne\n        ? swapExact0For1(amount, wallet.address)\n        : swapExact1For0(amount, wallet.address));\n\n      if (poke) await pool.burn(minTick, maxTick, 0);\n\n      const { amount0: fees0, amount1: fees1 } = await pool.collectStatic(\n        wallet.address,\n        minTick,\n        maxTick,\n        MaxUint128,\n        MaxUint128\n      );\n\n      expect(fees0, \"fees owed in token0 are greater than 0\").to.be.gte(0);\n      expect(fees1, \"fees owed in token1 are greater than 0\").to.be.gte(0);\n\n      return { token0Fees: fees0, token1Fees: fees1 };\n    }\n\n    it(\"position owner gets full fees when protocol fee is off\", async () => {\n      const { token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      });\n\n      // 6 bips * 1e18\n      expect(token0Fees).to.eq(\"499999999999999\");\n      expect(token1Fees).to.eq(0);\n    });\n\n    it(\"swap fees accumulate as expected (0 for 1)\", async () => {\n      let token0Fees;\n      let token1Fees;\n      ({ token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      }));\n      expect(token0Fees).to.eq(\"499999999999999\");\n      expect(token1Fees).to.eq(0);\n      ({ token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      }));\n      expect(token0Fees).to.eq(\"999999999999998\");\n      expect(token1Fees).to.eq(0);\n      ({ token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      }));\n      expect(token0Fees).to.eq(\"1499999999999997\");\n      expect(token1Fees).to.eq(0);\n    });\n\n    it(\"swap fees accumulate as expected (1 for 0)\", async () => {\n      let token0Fees;\n      let token1Fees;\n      ({ token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: false,\n        poke: true,\n      }));\n      expect(token0Fees).to.eq(0);\n      expect(token1Fees).to.eq(\"499999999999999\");\n      ({ token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: false,\n        poke: true,\n      }));\n      expect(token0Fees).to.eq(0);\n      expect(token1Fees).to.eq(\"999999999999998\");\n      ({ token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: false,\n        poke: true,\n      }));\n      expect(token0Fees).to.eq(0);\n      expect(token1Fees).to.eq(\"1499999999999997\");\n    });\n\n    it(\"position owner gets partial fees when protocol fee is on\", async () => {\n      await pool.setFeeProtocol(6, 6);\n\n      const { token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      });\n\n      expect(token0Fees).to.be.eq(\"416666666666666\");\n      expect(token1Fees).to.be.eq(0);\n    });\n\n    describe(\"#collectProtocol\", () => {\n      it(\"returns 0 if no fees\", async () => {\n        await pool.setFeeProtocol(6, 6);\n        const { amount0, amount1 } = await pool.collectProtocolStatic(\n          wallet.address,\n          MaxUint128,\n          MaxUint128\n        );\n        expect(amount0).to.be.eq(0);\n        expect(amount1).to.be.eq(0);\n      });\n\n      it(\"can collect fees\", async () => {\n        await pool.setFeeProtocol(6, 6);\n\n        await swapAndGetFeesOwed({\n          amount: expandTo18Decimals(1),\n          zeroForOne: true,\n          poke: true,\n        });\n\n        await expect(\n          pool.collectProtocol(other.address, MaxUint128, MaxUint128)\n        )\n          .to.emit(token0, \"Transfer\")\n          .withArgs(pool.address, other.address, \"83333333333332\");\n      });\n\n      it(\"fees collected can differ between token0 and token1\", async () => {\n        await pool.setFeeProtocol(8, 5);\n\n        await swapAndGetFeesOwed({\n          amount: expandTo18Decimals(1),\n          zeroForOne: true,\n          poke: false,\n        });\n        await swapAndGetFeesOwed({\n          amount: expandTo18Decimals(1),\n          zeroForOne: false,\n          poke: false,\n        });\n\n        await expect(\n          pool.collectProtocol(other.address, MaxUint128, MaxUint128)\n        )\n          .to.emit(token0, \"Transfer\")\n          // more token0 fees because it's 1/5th the swap fees\n          .withArgs(pool.address, other.address, \"62499999999999\")\n          .to.emit(token1, \"Transfer\")\n          // less token1 fees because it's 1/8th the swap fees\n          .withArgs(pool.address, other.address, \"99999999999998\");\n      });\n    });\n\n    it(\"fees collected by lp after two swaps should be double one swap\", async () => {\n      await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      });\n      const { token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      });\n\n      // 6 bips * 2e18\n      expect(token0Fees).to.eq(\"999999999999998\");\n      expect(token1Fees).to.eq(0);\n    });\n\n    it(\"fees collected after two swaps with fee turned on in middle are fees from last swap (not confiscatory)\", async () => {\n      await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: false,\n      });\n\n      await pool.setFeeProtocol(6, 6);\n\n      const { token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      });\n\n      expect(token0Fees).to.eq(\"916666666666666\");\n      expect(token1Fees).to.eq(0);\n    });\n\n    it(\"fees collected by lp after two swaps with intermediate withdrawal\", async () => {\n      await pool.setFeeProtocol(6, 6);\n\n      const { token0Fees, token1Fees } = await swapAndGetFeesOwed({\n        amount: expandTo18Decimals(1),\n        zeroForOne: true,\n        poke: true,\n      });\n\n      expect(token0Fees).to.eq(\"416666666666666\");\n      expect(token1Fees).to.eq(0);\n\n      // collect the fees\n      await pool.collect(\n        wallet.address,\n        minTick,\n        maxTick,\n        MaxUint128,\n        MaxUint128\n      );\n\n      const { token0Fees: token0FeesNext, token1Fees: token1FeesNext } =\n        await swapAndGetFeesOwed({\n          amount: expandTo18Decimals(1),\n          zeroForOne: true,\n          poke: false,\n        });\n\n      expect(token0FeesNext).to.eq(0);\n      expect(token1FeesNext).to.eq(0);\n\n      let { token0: token0ProtocolFees, token1: token1ProtocolFees } =\n        await pool.protocolFees();\n      expect(token0ProtocolFees).to.eq(\"166666666666666\");\n      expect(token1ProtocolFees).to.eq(0);\n\n      await pool.burn(minTick, maxTick, 0); // poke to update fees\n      await expect(\n        pool.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128)\n      )\n        .to.emit(token0, \"Transfer\")\n        .withArgs(pool.address, wallet.address, \"416666666666666\");\n      ({ token0: token0ProtocolFees, token1: token1ProtocolFees } =\n        await pool.protocolFees());\n      expect(token0ProtocolFees).to.eq(\"166666666666666\");\n      expect(token1ProtocolFees).to.eq(0);\n    });\n  });\n\n  describe(\"#tickSpacing\", () => {\n    describe(\"tickSpacing = 12\", () => {\n      beforeEach(\"deploy pool\", async () => {\n        pool = await createPool(FeeAmount.MEDIUM, 12);\n      });\n      describe(\"post initialize\", () => {\n        beforeEach(\"initialize pool\", async () => {\n          await pool.initialize(encodePriceSqrt(1, 1));\n        });\n        it(\"mint can only be called for multiples of 12\", async () => {\n          await expect(mint(wallet.address, -6, 0, 1)).to.be.reverted;\n          await expect(mint(wallet.address, 0, 6, 1)).to.be.reverted;\n        });\n        it(\"mint can be called with multiples of 12\", async () => {\n          await mint(wallet.address, 12, 24, 1);\n          await mint(wallet.address, -144, -120, 1);\n        });\n        it(\"swapping across gaps works in 1 for 0 direction\", async () => {\n          const liquidityAmount = expandTo18Decimals(1).div(4);\n          await mint(wallet.address, 120000, 121200, liquidityAmount);\n          await swapExact1For0(expandTo18Decimals(1), wallet.address);\n          await expect(pool.burn(120000, 121200, liquidityAmount))\n            .to.emit(pool, \"Burn\")\n            .withArgs(\n              wallet.address,\n              120000,\n              121200,\n              liquidityAmount,\n              \"30027458295511\",\n              \"996999999999999999\"\n            )\n            .to.not.emit(token0, \"Transfer\")\n            .to.not.emit(token1, \"Transfer\");\n          expect((await pool.slot0()).tick).to.eq(120196);\n        });\n        it(\"swapping across gaps works in 0 for 1 direction\", async () => {\n          const liquidityAmount = expandTo18Decimals(1).div(4);\n          await mint(wallet.address, -121200, -120000, liquidityAmount);\n          await swapExact0For1(expandTo18Decimals(1), wallet.address);\n          await expect(pool.burn(-121200, -120000, liquidityAmount))\n            .to.emit(pool, \"Burn\")\n            .withArgs(\n              wallet.address,\n              -121200,\n              -120000,\n              liquidityAmount,\n              \"996999999999999999\",\n              \"30027458295511\"\n            )\n            .to.not.emit(token0, \"Transfer\")\n            .to.not.emit(token1, \"Transfer\");\n          expect((await pool.slot0()).tick).to.eq(-120197);\n        });\n      });\n    });\n  });\n\n  // https://github.com/Uniswap/uniswap-v3-core/issues/214\n  it(\"tick transition cannot run twice if zero for one swap ends at fractional price just below tick\", async () => {\n    pool = await createPool(FeeAmount.MEDIUM, 1);\n    const sqrtTickMath = (await (\n      await ethers.getContractFactory(\"TickMathTest\")\n    ).deploy()) as TickMathTest;\n    const swapMath = (await (\n      await ethers.getContractFactory(\"SwapMathTest\")\n    ).deploy()) as SwapMathTest;\n    const p0 = (await sqrtTickMath.getSqrtRatioAtTick(-24081)).add(1);\n    // initialize at a price of ~0.3 token1/token0\n    // meaning if you swap in 2 token0, you should end up getting 0 token1\n    await pool.initialize(p0);\n    expect(await pool.liquidity(), \"current pool liquidity is 1\").to.eq(0);\n    expect((await pool.slot0()).tick, \"pool tick is -24081\").to.eq(-24081);\n\n    // add a bunch of liquidity around current price\n    const liquidity = expandTo18Decimals(1000);\n    await mint(wallet.address, -24082, -24080, liquidity);\n    expect(\n      await pool.liquidity(),\n      \"current pool liquidity is now liquidity + 1\"\n    ).to.eq(liquidity);\n\n    await mint(wallet.address, -24082, -24081, liquidity);\n    expect(\n      await pool.liquidity(),\n      \"current pool liquidity is still liquidity + 1\"\n    ).to.eq(liquidity);\n\n    // check the math works out to moving the price down 1, sending no amount out, and having some amount remaining\n    {\n      const { feeAmount, amountIn, amountOut, sqrtQ } =\n        await swapMath.computeSwapStep(\n          p0,\n          p0.sub(1),\n          liquidity,\n          3,\n          FeeAmount.MEDIUM\n        );\n      expect(sqrtQ, \"price moves\").to.eq(p0.sub(1));\n      expect(feeAmount, \"fee amount is 1\").to.eq(1);\n      expect(amountIn, \"amount in is 1\").to.eq(1);\n      expect(amountOut, \"zero amount out\").to.eq(0);\n    }\n\n    // swap 2 amount in, should get 0 amount out\n    await expect(swapExact0For1(3, wallet.address))\n      .to.emit(token0, \"Transfer\")\n      .withArgs(wallet.address, pool.address, 3)\n      .to.not.emit(token1, \"Transfer\");\n\n    const { tick, sqrtPriceX96 } = await pool.slot0();\n\n    expect(tick, \"pool is at the next tick\").to.eq(-24082);\n    expect(sqrtPriceX96, \"pool price is still on the p0 boundary\").to.eq(\n      p0.sub(1)\n    );\n    expect(\n      await pool.liquidity(),\n      \"pool has run tick transition and liquidity changed\"\n    ).to.eq(liquidity.mul(2));\n  });\n\n  describe(\"#setFeeProtocol\", () => {\n    beforeEach(\"initialize the pool\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n    });\n\n    it(\"can only be called by factory owner\", async () => {\n      await expect(pool.connect(other).setFeeProtocol(5, 5)).to.be.reverted;\n    });\n    it(\"fails if fee is lt 4 or gt 10\", async () => {\n      await expect(pool.setFeeProtocol(3, 3)).to.be.reverted;\n      await expect(pool.setFeeProtocol(6, 3)).to.be.reverted;\n      await expect(pool.setFeeProtocol(3, 6)).to.be.reverted;\n      await expect(pool.setFeeProtocol(11, 11)).to.be.reverted;\n      await expect(pool.setFeeProtocol(6, 11)).to.be.reverted;\n      await expect(pool.setFeeProtocol(11, 6)).to.be.reverted;\n    });\n    it(\"succeeds for fee of 4\", async () => {\n      await pool.setFeeProtocol(4, 4);\n    });\n    it(\"succeeds for fee of 10\", async () => {\n      await pool.setFeeProtocol(10, 10);\n    });\n    it(\"sets protocol fee\", async () => {\n      await pool.setFeeProtocol(7, 7);\n      expect((await pool.slot0()).feeProtocol).to.eq(119);\n    });\n    it(\"can change protocol fee\", async () => {\n      await pool.setFeeProtocol(7, 7);\n      await pool.setFeeProtocol(5, 8);\n      expect((await pool.slot0()).feeProtocol).to.eq(133);\n    });\n    it(\"can turn off protocol fee\", async () => {\n      await pool.setFeeProtocol(4, 4);\n      await pool.setFeeProtocol(0, 0);\n      expect((await pool.slot0()).feeProtocol).to.eq(0);\n    });\n    it(\"emits an event when turned on\", async () => {\n      await expect(pool.setFeeProtocol(7, 7))\n        .to.be.emit(pool, \"SetFeeProtocol\")\n        .withArgs(0, 0, 7, 7);\n    });\n    it(\"emits an event when turned off\", async () => {\n      await pool.setFeeProtocol(7, 5);\n      await expect(pool.setFeeProtocol(0, 0))\n        .to.be.emit(pool, \"SetFeeProtocol\")\n        .withArgs(7, 5, 0, 0);\n    });\n    it(\"emits an event when changed\", async () => {\n      await pool.setFeeProtocol(4, 10);\n      await expect(pool.setFeeProtocol(6, 8))\n        .to.be.emit(pool, \"SetFeeProtocol\")\n        .withArgs(4, 10, 6, 8);\n    });\n    it(\"emits an event when unchanged\", async () => {\n      await pool.setFeeProtocol(5, 9);\n      await expect(pool.setFeeProtocol(5, 9))\n        .to.be.emit(pool, \"SetFeeProtocol\")\n        .withArgs(5, 9, 5, 9);\n    });\n  });\n\n  describe(\"fees overflow scenarios\", async () => {\n    it(\"up to max uint 128\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await mint(wallet.address, minTick, maxTick, 1);\n      await flash(0, 0, wallet.address, MaxUint128, MaxUint128);\n\n      const [feeGrowthGlobal0X128, feeGrowthGlobal1X128] = await Promise.all([\n        pool.feeGrowthGlobal0X128(),\n        pool.feeGrowthGlobal1X128(),\n      ]);\n      // all 1s in first 128 bits\n      expect(feeGrowthGlobal0X128).to.eq(MaxUint128.shl(128));\n      expect(feeGrowthGlobal1X128).to.eq(MaxUint128.shl(128));\n      await pool.burn(minTick, maxTick, 0);\n      const { amount0, amount1 } = await pool.collectStatic(\n        wallet.address,\n        minTick,\n        maxTick,\n        MaxUint128,\n        MaxUint128\n      );\n      expect(amount0).to.eq(MaxUint128);\n      expect(amount1).to.eq(MaxUint128);\n    });\n\n    it(\"overflow max uint 128\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await mint(wallet.address, minTick, maxTick, 1);\n      await flash(0, 0, wallet.address, MaxUint128, MaxUint128);\n      await flash(0, 0, wallet.address, 1, 1);\n\n      const [feeGrowthGlobal0X128, feeGrowthGlobal1X128] = await Promise.all([\n        pool.feeGrowthGlobal0X128(),\n        pool.feeGrowthGlobal1X128(),\n      ]);\n      // all 1s in first 128 bits\n      expect(feeGrowthGlobal0X128).to.eq(0);\n      expect(feeGrowthGlobal1X128).to.eq(0);\n      await pool.burn(minTick, maxTick, 0);\n      const { amount0, amount1 } = await pool.collectStatic(\n        wallet.address,\n        minTick,\n        maxTick,\n        MaxUint128,\n        MaxUint128\n      );\n      // fees burned\n      expect(amount0).to.eq(0);\n      expect(amount1).to.eq(0);\n    });\n\n    it(\"overflow max uint 128 after poke burns fees owed to 0\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await mint(wallet.address, minTick, maxTick, 1);\n      await flash(0, 0, wallet.address, MaxUint128, MaxUint128);\n      await pool.burn(minTick, maxTick, 0);\n      await flash(0, 0, wallet.address, 1, 1);\n      await pool.burn(minTick, maxTick, 0);\n\n      const { amount0, amount1 } = await pool.collectStatic(\n        wallet.address,\n        minTick,\n        maxTick,\n        MaxUint128,\n        MaxUint128\n      );\n      // fees burned\n      expect(amount0).to.eq(0);\n      expect(amount1).to.eq(0);\n    });\n\n    it(\"two positions at the same snapshot\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await mint(wallet.address, minTick, maxTick, 1);\n      await mint(other.address, minTick, maxTick, 1);\n      await flash(0, 0, wallet.address, MaxUint128, 0);\n      await flash(0, 0, wallet.address, MaxUint128, 0);\n      const feeGrowthGlobal0X128 = await pool.feeGrowthGlobal0X128();\n      expect(feeGrowthGlobal0X128).to.eq(MaxUint128.shl(128));\n      await flash(0, 0, wallet.address, 2, 0);\n      await pool.burn(minTick, maxTick, 0);\n      await pool.connect(other).burn(minTick, maxTick, 0);\n      let { amount0 } = await pool.collectStatic(\n        wallet.address,\n        minTick,\n        maxTick,\n        MaxUint128,\n        MaxUint128\n      );\n      expect(amount0, \"amount0 of wallet\").to.eq(0);\n      ({ amount0 } = await pool\n        .connect(other)\n        .collectStatic(\n          other.address,\n          minTick,\n          maxTick,\n          MaxUint128,\n          MaxUint128\n        ));\n      expect(amount0, \"amount0 of other\").to.eq(0);\n    });\n\n    it(\"two positions 1 wei of fees apart overflows exactly once\", async () => {\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await mint(wallet.address, minTick, maxTick, 1);\n      await flash(0, 0, wallet.address, 1, 0);\n      await mint(other.address, minTick, maxTick, 1);\n      await flash(0, 0, wallet.address, MaxUint128, 0);\n      await flash(0, 0, wallet.address, MaxUint128, 0);\n      const feeGrowthGlobal0X128 = await pool.feeGrowthGlobal0X128();\n      expect(feeGrowthGlobal0X128).to.eq(0);\n      await flash(0, 0, wallet.address, 2, 0);\n      await pool.burn(minTick, maxTick, 0);\n      await pool.connect(other).burn(minTick, maxTick, 0);\n      let { amount0 } = await pool.collectStatic(\n        wallet.address,\n        minTick,\n        maxTick,\n        MaxUint128,\n        MaxUint128\n      );\n      expect(amount0, \"amount0 of wallet\").to.eq(1);\n      ({ amount0 } = await pool\n        .connect(other)\n        .collectStatic(\n          other.address,\n          minTick,\n          maxTick,\n          MaxUint128,\n          MaxUint128\n        ));\n      expect(amount0, \"amount0 of other\").to.eq(0);\n    });\n  });\n\n  describe(\"swap underpayment tests\", () => {\n    let underpay: TestUniswapV3SwapPay;\n    beforeEach(\"deploy swap test\", async () => {\n      const underpayFactory = await ethers.getContractFactory(\n        \"TestUniswapV3SwapPay\"\n      );\n      underpay = (await underpayFactory.deploy()) as TestUniswapV3SwapPay;\n      await token0.approve(underpay.address, constants.MaxUint256);\n      await token1.approve(underpay.address, constants.MaxUint256);\n      await pool.initialize(encodePriceSqrt(1, 1));\n      await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1));\n    });\n\n    it(\"underpay zero for one and exact in\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          true,\n          MIN_SQRT_RATIO.add(1),\n          1000,\n          1,\n          0\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"pay in the wrong token zero for one and exact in\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          true,\n          MIN_SQRT_RATIO.add(1),\n          1000,\n          0,\n          2000\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"overpay zero for one and exact in\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          true,\n          MIN_SQRT_RATIO.add(1),\n          1000,\n          2000,\n          0\n        )\n      ).to.not.be.revertedWith(\"IIA\");\n    });\n    it(\"underpay zero for one and exact out\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          true,\n          MIN_SQRT_RATIO.add(1),\n          -1000,\n          1,\n          0\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"pay in the wrong token zero for one and exact out\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          true,\n          MIN_SQRT_RATIO.add(1),\n          -1000,\n          0,\n          2000\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"overpay zero for one and exact out\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          true,\n          MIN_SQRT_RATIO.add(1),\n          -1000,\n          2000,\n          0\n        )\n      ).to.not.be.revertedWith(\"IIA\");\n    });\n    it(\"underpay one for zero and exact in\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          false,\n          MAX_SQRT_RATIO.sub(1),\n          1000,\n          0,\n          1\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"pay in the wrong token one for zero and exact in\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          false,\n          MAX_SQRT_RATIO.sub(1),\n          1000,\n          2000,\n          0\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"overpay one for zero and exact in\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          false,\n          MAX_SQRT_RATIO.sub(1),\n          1000,\n          0,\n          2000\n        )\n      ).to.not.be.revertedWith(\"IIA\");\n    });\n    it(\"underpay one for zero and exact out\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          false,\n          MAX_SQRT_RATIO.sub(1),\n          -1000,\n          0,\n          1\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"pay in the wrong token one for zero and exact out\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          false,\n          MAX_SQRT_RATIO.sub(1),\n          -1000,\n          2000,\n          0\n        )\n      ).to.be.revertedWith(\"IIA\");\n    });\n    it(\"overpay one for zero and exact out\", async () => {\n      await expect(\n        underpay.swap(\n          pool.address,\n          wallet.address,\n          false,\n          MAX_SQRT_RATIO.sub(1),\n          -1000,\n          0,\n          2000\n        )\n      ).to.not.be.revertedWith(\"IIA\");\n    });\n  });\n});\n"
  },
  {
    "path": "test/UniswapV3Pool.swaps.spec.ts.pending",
    "content": "import { Decimal } from \"decimal.js\";\nimport { BigNumber, BigNumberish, ContractTransaction, Wallet } from \"ethers\";\nimport { ethers, waffle } from \"hardhat\";\nimport { MockTimeUniswapV3Pool } from \"../typechain/MockTimeUniswapV3Pool\";\nimport { TestERC20 } from \"../typechain/TestERC20\";\n\nimport { TestUniswapV3Callee } from \"../typechain/TestUniswapV3Callee\";\nimport { expect } from \"./shared/expect\";\nimport { poolFixture } from \"./shared/fixtures\";\nimport { formatPrice, formatTokenAmount } from \"./shared/format\";\nimport {\n  createPoolFunctions,\n  encodePriceSqrt,\n  expandTo18Decimals,\n  FeeAmount,\n  getMaxLiquidityPerTick,\n  getMaxTick,\n  getMinTick,\n  MAX_SQRT_RATIO,\n  MaxUint128,\n  MIN_SQRT_RATIO,\n  TICK_SPACINGS,\n} from \"./shared/utilities\";\n\nDecimal.config({ toExpNeg: -500, toExpPos: 500 });\n\nconst createFixtureLoader = waffle.createFixtureLoader;\nconst { constants } = ethers;\n\ninterface BaseSwapTestCase {\n  zeroForOne: boolean;\n  sqrtPriceLimit?: BigNumber;\n}\ninterface SwapExact0For1TestCase extends BaseSwapTestCase {\n  zeroForOne: true;\n  exactOut: false;\n  amount0: BigNumberish;\n  sqrtPriceLimit?: BigNumber;\n}\ninterface SwapExact1For0TestCase extends BaseSwapTestCase {\n  zeroForOne: false;\n  exactOut: false;\n  amount1: BigNumberish;\n  sqrtPriceLimit?: BigNumber;\n}\ninterface Swap0ForExact1TestCase extends BaseSwapTestCase {\n  zeroForOne: true;\n  exactOut: true;\n  amount1: BigNumberish;\n  sqrtPriceLimit?: BigNumber;\n}\ninterface Swap1ForExact0TestCase extends BaseSwapTestCase {\n  zeroForOne: false;\n  exactOut: true;\n  amount0: BigNumberish;\n  sqrtPriceLimit?: BigNumber;\n}\ninterface SwapToHigherPrice extends BaseSwapTestCase {\n  zeroForOne: false;\n  sqrtPriceLimit: BigNumber;\n}\ninterface SwapToLowerPrice extends BaseSwapTestCase {\n  zeroForOne: true;\n  sqrtPriceLimit: BigNumber;\n}\ntype SwapTestCase =\n  | SwapExact0For1TestCase\n  | Swap0ForExact1TestCase\n  | SwapExact1For0TestCase\n  | Swap1ForExact0TestCase\n  | SwapToHigherPrice\n  | SwapToLowerPrice;\n\nfunction swapCaseToDescription(testCase: SwapTestCase): string {\n  const priceClause = testCase?.sqrtPriceLimit\n    ? ` to price ${formatPrice(testCase.sqrtPriceLimit)}`\n    : \"\";\n  if (\"exactOut\" in testCase) {\n    if (testCase.exactOut) {\n      if (testCase.zeroForOne) {\n        return `swap token0 for exactly ${formatTokenAmount(\n          testCase.amount1\n        )} token1${priceClause}`;\n      } else {\n        return `swap token1 for exactly ${formatTokenAmount(\n          testCase.amount0\n        )} token0${priceClause}`;\n      }\n    } else {\n      if (testCase.zeroForOne) {\n        return `swap exactly ${formatTokenAmount(\n          testCase.amount0\n        )} token0 for token1${priceClause}`;\n      } else {\n        return `swap exactly ${formatTokenAmount(\n          testCase.amount1\n        )} token1 for token0${priceClause}`;\n      }\n    }\n  } else {\n    if (testCase.zeroForOne) {\n      return `swap token0 for token1${priceClause}`;\n    } else {\n      return `swap token1 for token0${priceClause}`;\n    }\n  }\n}\n\ntype PoolFunctions = ReturnType<typeof createPoolFunctions>;\n\n// can't use address zero because the ERC20 token does not allow it\nconst SWAP_RECIPIENT_ADDRESS = constants.AddressZero.slice(0, -1) + \"1\";\nconst POSITION_PROCEEDS_OUTPUT_ADDRESS =\n  constants.AddressZero.slice(0, -1) + \"2\";\n\nasync function executeSwap(\n  pool: MockTimeUniswapV3Pool,\n  testCase: SwapTestCase,\n  poolFunctions: PoolFunctions\n): Promise<ContractTransaction> {\n  let swap: ContractTransaction;\n  if (\"exactOut\" in testCase) {\n    if (testCase.exactOut) {\n      if (testCase.zeroForOne) {\n        swap = await poolFunctions.swap0ForExact1(\n          testCase.amount1,\n          SWAP_RECIPIENT_ADDRESS,\n          testCase.sqrtPriceLimit\n        );\n      } else {\n        swap = await poolFunctions.swap1ForExact0(\n          testCase.amount0,\n          SWAP_RECIPIENT_ADDRESS,\n          testCase.sqrtPriceLimit\n        );\n      }\n    } else {\n      if (testCase.zeroForOne) {\n        swap = await poolFunctions.swapExact0For1(\n          testCase.amount0,\n          SWAP_RECIPIENT_ADDRESS,\n          testCase.sqrtPriceLimit\n        );\n      } else {\n        swap = await poolFunctions.swapExact1For0(\n          testCase.amount1,\n          SWAP_RECIPIENT_ADDRESS,\n          testCase.sqrtPriceLimit\n        );\n      }\n    }\n  } else {\n    if (testCase.zeroForOne) {\n      swap = await poolFunctions.swapToLowerPrice(\n        testCase.sqrtPriceLimit,\n        SWAP_RECIPIENT_ADDRESS\n      );\n    } else {\n      swap = await poolFunctions.swapToHigherPrice(\n        testCase.sqrtPriceLimit,\n        SWAP_RECIPIENT_ADDRESS\n      );\n    }\n  }\n  return swap;\n}\n\nconst DEFAULT_POOL_SWAP_TESTS: SwapTestCase[] = [\n  // swap large amounts in/out\n  {\n    zeroForOne: true,\n    exactOut: false,\n    amount0: expandTo18Decimals(1),\n  },\n  {\n    zeroForOne: false,\n    exactOut: false,\n    amount1: expandTo18Decimals(1),\n  },\n  {\n    zeroForOne: true,\n    exactOut: true,\n    amount1: expandTo18Decimals(1),\n  },\n  {\n    zeroForOne: false,\n    exactOut: true,\n    amount0: expandTo18Decimals(1),\n  },\n  // swap large amounts in/out with a price limit\n  {\n    zeroForOne: true,\n    exactOut: false,\n    amount0: expandTo18Decimals(1),\n    sqrtPriceLimit: encodePriceSqrt(50, 100),\n  },\n  {\n    zeroForOne: false,\n    exactOut: false,\n    amount1: expandTo18Decimals(1),\n    sqrtPriceLimit: encodePriceSqrt(200, 100),\n  },\n  {\n    zeroForOne: true,\n    exactOut: true,\n    amount1: expandTo18Decimals(1),\n    sqrtPriceLimit: encodePriceSqrt(50, 100),\n  },\n  {\n    zeroForOne: false,\n    exactOut: true,\n    amount0: expandTo18Decimals(1),\n    sqrtPriceLimit: encodePriceSqrt(200, 100),\n  },\n  // swap small amounts in/out\n  {\n    zeroForOne: true,\n    exactOut: false,\n    amount0: 1000,\n  },\n  {\n    zeroForOne: false,\n    exactOut: false,\n    amount1: 1000,\n  },\n  {\n    zeroForOne: true,\n    exactOut: true,\n    amount1: 1000,\n  },\n  {\n    zeroForOne: false,\n    exactOut: true,\n    amount0: 1000,\n  },\n  // swap arbitrary input to price\n  {\n    sqrtPriceLimit: encodePriceSqrt(5, 2),\n    zeroForOne: false,\n  },\n  {\n    sqrtPriceLimit: encodePriceSqrt(2, 5),\n    zeroForOne: true,\n  },\n  {\n    sqrtPriceLimit: encodePriceSqrt(5, 2),\n    zeroForOne: true,\n  },\n  {\n    sqrtPriceLimit: encodePriceSqrt(2, 5),\n    zeroForOne: false,\n  },\n];\n\ninterface Position {\n  tickLower: number;\n  tickUpper: number;\n  liquidity: BigNumberish;\n}\n\ninterface PoolTestCase {\n  description: string;\n  feeAmount: number;\n  tickSpacing: number;\n  startingPrice: BigNumber;\n  positions: Position[];\n  swapTests?: SwapTestCase[];\n}\n\nconst TEST_POOLS: PoolTestCase[] = [\n  {\n    description: \"low fee, 1:1 price, 2e18 max range liquidity\",\n    feeAmount: FeeAmount.LOW,\n    tickSpacing: TICK_SPACINGS[FeeAmount.LOW],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.LOW]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.LOW]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"medium fee, 1:1 price, 2e18 max range liquidity\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"high fee, 1:1 price, 2e18 max range liquidity\",\n    feeAmount: FeeAmount.HIGH,\n    tickSpacing: TICK_SPACINGS[FeeAmount.HIGH],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.HIGH]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.HIGH]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"medium fee, 10:1 price, 2e18 max range liquidity\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(10, 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"medium fee, 1:10 price, 2e18 max range liquidity\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, 10),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description:\n      \"medium fee, 1:1 price, 0 liquidity, all liquidity around current price\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: -TICK_SPACINGS[FeeAmount.MEDIUM],\n        liquidity: expandTo18Decimals(2),\n      },\n      {\n        tickLower: TICK_SPACINGS[FeeAmount.MEDIUM],\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description:\n      \"medium fee, 1:1 price, additional liquidity around current price\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: -TICK_SPACINGS[FeeAmount.MEDIUM],\n        liquidity: expandTo18Decimals(2),\n      },\n      {\n        tickLower: TICK_SPACINGS[FeeAmount.MEDIUM],\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"low fee, large liquidity around current price (stable swap)\",\n    feeAmount: FeeAmount.LOW,\n    tickSpacing: TICK_SPACINGS[FeeAmount.LOW],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: -TICK_SPACINGS[FeeAmount.LOW],\n        tickUpper: TICK_SPACINGS[FeeAmount.LOW],\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"medium fee, token0 liquidity only\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: 0,\n        tickUpper: 2000 * TICK_SPACINGS[FeeAmount.MEDIUM],\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"medium fee, token1 liquidity only\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: -2000 * TICK_SPACINGS[FeeAmount.MEDIUM],\n        tickUpper: 0,\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"close to max price\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(BigNumber.from(2).pow(127), 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"close to min price\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, BigNumber.from(2).pow(127)),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"max full range liquidity at 1:1 price with default fee\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: encodePriceSqrt(1, 1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n      },\n    ],\n  },\n  {\n    description: \"initialized at the max ratio\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: MAX_SQRT_RATIO.sub(1),\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n  {\n    description: \"initialized at the min ratio\",\n    feeAmount: FeeAmount.MEDIUM,\n    tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],\n    startingPrice: MIN_SQRT_RATIO,\n    positions: [\n      {\n        tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),\n        liquidity: expandTo18Decimals(2),\n      },\n    ],\n  },\n];\n\ndescribe(\"UniswapV3Pool swap tests\", () => {\n  let wallet: Wallet, other: Wallet;\n\n  let loadFixture: ReturnType<typeof createFixtureLoader>;\n\n  before(\"create fixture loader\", async () => {\n    [wallet, other] = await (ethers as any).getSigners();\n\n    loadFixture = createFixtureLoader([wallet]);\n  });\n\n  for (const poolCase of TEST_POOLS) {\n    describe(poolCase.description, () => {\n      const poolCaseFixture = async () => {\n        const {\n          createPool,\n          token0,\n          token1,\n          swapTargetCallee: swapTarget,\n        } = await poolFixture([wallet], waffle.provider);\n        const pool = await createPool(poolCase.feeAmount, poolCase.tickSpacing);\n        const poolFunctions = createPoolFunctions({\n          swapTarget,\n          token0,\n          token1,\n          pool,\n        });\n        await pool.initialize(poolCase.startingPrice);\n        // mint all positions\n        for (const position of poolCase.positions) {\n          await poolFunctions.mint(\n            wallet.address,\n            position.tickLower,\n            position.tickUpper,\n            position.liquidity\n          );\n        }\n\n        const [poolBalance0, poolBalance1] = await Promise.all([\n          token0.balanceOf(pool.address),\n          token1.balanceOf(pool.address),\n        ]);\n\n        return {\n          token0,\n          token1,\n          pool,\n          poolFunctions,\n          poolBalance0,\n          poolBalance1,\n          swapTarget,\n        };\n      };\n\n      let token0: TestERC20;\n      let token1: TestERC20;\n\n      let poolBalance0: BigNumber;\n      let poolBalance1: BigNumber;\n\n      let pool: MockTimeUniswapV3Pool;\n      let swapTarget: TestUniswapV3Callee;\n      let poolFunctions: PoolFunctions;\n\n      beforeEach(\"load fixture\", async () => {\n        ({\n          token0,\n          token1,\n          pool,\n          poolFunctions,\n          poolBalance0,\n          poolBalance1,\n          swapTarget,\n        } = await loadFixture(poolCaseFixture));\n      });\n\n      afterEach(\"check can burn positions\", async () => {\n        for (const { liquidity, tickUpper, tickLower } of poolCase.positions) {\n          await pool.burn(tickLower, tickUpper, liquidity);\n          await pool.collect(\n            POSITION_PROCEEDS_OUTPUT_ADDRESS,\n            tickLower,\n            tickUpper,\n            MaxUint128,\n            MaxUint128\n          );\n        }\n      });\n\n      for (const testCase of poolCase.swapTests ?? DEFAULT_POOL_SWAP_TESTS) {\n        it(swapCaseToDescription(testCase), async () => {\n          const slot0 = await pool.slot0();\n          const tx = executeSwap(pool, testCase, poolFunctions);\n          try {\n            await tx;\n          } catch (error) {\n            expect({\n              swapError: error.message,\n              poolBalance0: poolBalance0.toString(),\n              poolBalance1: poolBalance1.toString(),\n              poolPriceBefore: formatPrice(slot0.sqrtPriceX96),\n              tickBefore: slot0.tick,\n            }).to.matchSnapshot(\"swap error\");\n            return;\n          }\n          const [\n            poolBalance0After,\n            poolBalance1After,\n            slot0After,\n            liquidityAfter,\n            feeGrowthGlobal0X128,\n            feeGrowthGlobal1X128,\n          ] = await Promise.all([\n            token0.balanceOf(pool.address),\n            token1.balanceOf(pool.address),\n            pool.slot0(),\n            pool.liquidity(),\n            pool.feeGrowthGlobal0X128(),\n            pool.feeGrowthGlobal1X128(),\n          ]);\n          const poolBalance0Delta = poolBalance0After.sub(poolBalance0);\n          const poolBalance1Delta = poolBalance1After.sub(poolBalance1);\n\n          // check all the events were emitted corresponding to balance changes\n          if (poolBalance0Delta.eq(0))\n            await expect(tx).to.not.emit(token0, \"Transfer\");\n          else if (poolBalance0Delta.lt(0))\n            await expect(tx)\n              .to.emit(token0, \"Transfer\")\n              .withArgs(\n                pool.address,\n                SWAP_RECIPIENT_ADDRESS,\n                poolBalance0Delta.mul(-1)\n              );\n          else\n            await expect(tx)\n              .to.emit(token0, \"Transfer\")\n              .withArgs(wallet.address, pool.address, poolBalance0Delta);\n\n          if (poolBalance1Delta.eq(0))\n            await expect(tx).to.not.emit(token1, \"Transfer\");\n          else if (poolBalance1Delta.lt(0))\n            await expect(tx)\n              .to.emit(token1, \"Transfer\")\n              .withArgs(\n                pool.address,\n                SWAP_RECIPIENT_ADDRESS,\n                poolBalance1Delta.mul(-1)\n              );\n          else\n            await expect(tx)\n              .to.emit(token1, \"Transfer\")\n              .withArgs(wallet.address, pool.address, poolBalance1Delta);\n\n          // check that the swap event was emitted too\n          await expect(tx)\n            .to.emit(pool, \"Swap\")\n            .withArgs(\n              swapTarget.address,\n              SWAP_RECIPIENT_ADDRESS,\n              poolBalance0Delta,\n              poolBalance1Delta,\n              slot0After.sqrtPriceX96,\n              liquidityAfter,\n              slot0After.tick\n            );\n\n          const executionPrice = new Decimal(poolBalance1Delta.toString())\n            .div(poolBalance0Delta.toString())\n            .mul(-1);\n\n          expect({\n            amount0Before: poolBalance0.toString(),\n            amount1Before: poolBalance1.toString(),\n            amount0Delta: poolBalance0Delta.toString(),\n            amount1Delta: poolBalance1Delta.toString(),\n            feeGrowthGlobal0X128Delta: feeGrowthGlobal0X128.toString(),\n            feeGrowthGlobal1X128Delta: feeGrowthGlobal1X128.toString(),\n            tickBefore: slot0.tick,\n            poolPriceBefore: formatPrice(slot0.sqrtPriceX96),\n            tickAfter: slot0After.tick,\n            poolPriceAfter: formatPrice(slot0After.sqrtPriceX96),\n            executionPrice: executionPrice.toPrecision(5),\n          }).to.matchSnapshot(\"balances\");\n        });\n      }\n    });\n  }\n});\n"
  },
  {
    "path": "test/contracts/CorePool.test.ts",
    "content": "import { ethers, waffle } from \"hardhat\";\nimport { Wallet } from \"ethers\";\nimport type { UniswapV3Factory2, UniswapV3Pool2 } from \"../typechain\";\nimport { FeeAmount } from \"../../src/enum/FeeAmount\";\n\ndescribe(\"Test Uniswap v3 CorePool\", function () {\n  const createFixtureLoader = waffle.createFixtureLoader;\n  let loadFixture: ReturnType<typeof createFixtureLoader>;\n  let deployer: Wallet;\n  let uniswapV3PoolAddressOnMainnet =\n    \"0x92560C178cE069CC014138eD3C2F5221Ba71f58a\";\n  //  \"0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8\";\n  let sqrtPriceX96ForInitialization = \"2505290050365003892876723467\";\n\n  let tokenA: string = \"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48\";\n  let tokenB: string = \"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\";\n  let fee: number = FeeAmount.MEDIUM;\n  let tickSpacing = 60;\n\n  let uniswapV3Factory: UniswapV3Factory2;\n  let uniswapV3Pool: UniswapV3Pool2;\n\n  before(\"create fixture loader\", async function () {\n    [deployer] = await (ethers as any).getSigners();\n    loadFixture = createFixtureLoader([deployer]);\n  });\n\n  interface FactoryFixture {\n    uniswapV3Factory: UniswapV3Factory2;\n  }\n\n  interface PoolFixture extends FactoryFixture {\n    uniswapV3Pool: UniswapV3Pool2;\n  }\n\n  async function fixture(wallets: Wallet[]): Promise<PoolFixture> {\n    // if we want to test contract identical to Uniswap v3-core repo, use this\n\n    // import {\n    //   abi as FACTORY_ABI,\n    //   bytecode as FACTORY_BYTECODE,\n    // } from \"@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json\";\n\n    // let contractFactory = new ContractFactory(\n    //   FACTORY_ABI,\n    //   FACTORY_BYTECODE,\n    //   wallets[0]\n    // );\n    // let uniswapV3Factory = (await contractFactory.deploy()) as UniswapV3Factory2;\n\n    let uniswapV3FactoryFactory = await ethers.getContractFactory(\n      \"UniswapV3Factory2\"\n    );\n    let uniswapV3Factory =\n      (await uniswapV3FactoryFactory.deploy()) as UniswapV3Factory2;\n    await uniswapV3Factory.deployed();\n\n    await uniswapV3Factory.createPool(tokenA, tokenB, fee);\n    let poolAddress = await uniswapV3Factory.getPool(tokenA, tokenB, fee);\n\n    let uniswapV3Pool = (await ethers.getContractAt(\n      \"UniswapV3Pool2\",\n      poolAddress\n    )) as UniswapV3Pool2;\n\n    await uniswapV3Pool.initialize(sqrtPriceX96ForInitialization);\n\n    return { uniswapV3Factory, uniswapV3Pool };\n  }\n\n  beforeEach(\"deploy fixture\", async function () {\n    ({ uniswapV3Factory, uniswapV3Pool } = await loadFixture(fixture));\n  });\n\n  it(\"can be deployed succussfully\", async function () {\n    console.log(uniswapV3Factory.address);\n    console.log(uniswapV3Pool.address);\n    console.log(await uniswapV3Pool.tickSpacing());\n  });\n\n  it(\"results on swap event id 15488\", async function () {\n    // setup corepool states as event id 15487 finishes\n    let uniswapV3PoolOnMainnet = (await ethers.getContractAt(\n      \"UniswapV3Pool2\",\n      uniswapV3PoolAddressOnMainnet\n    )) as UniswapV3Pool2;\n    let minTick = 199080; // -887220;\n    let maxTick = 200280; // 887220;\n    let liquidity = await uniswapV3PoolOnMainnet.liquidity();\n    let feeGrowthGlobal0X128 =\n      await uniswapV3PoolOnMainnet.feeGrowthGlobal0X128();\n    let feeGrowthGlobal1X128 =\n      await uniswapV3PoolOnMainnet.feeGrowthGlobal1X128();\n    let slot0OnMainnet = await uniswapV3PoolOnMainnet.slot0();\n    let tickCurrent = slot0OnMainnet.tick;\n    let sqrtPriceX96 = slot0OnMainnet.sqrtPriceX96;\n\n    await uniswapV3Pool.setState(\n      tickCurrent,\n      sqrtPriceX96,\n      liquidity,\n      feeGrowthGlobal0X128,\n      feeGrowthGlobal1X128\n    );\n    console.log(\"pool global state setup successfully!\");\n\n    // let relatedTicks = findRelatedTicks(minTick, maxTick);\n    let relatedTicks = [49800, 64020];\n    let relatedTickBitmapWordPoses =\n      findRelatedTickBitmapWordPoses(relatedTicks);\n    for (let tickIndex of relatedTicks) {\n      let tickInfo = await uniswapV3PoolOnMainnet.ticks(tickIndex);\n      await uniswapV3Pool.setTickInfo(\n        tickIndex,\n        tickInfo.liquidityGross,\n        tickInfo.liquidityNet,\n        tickInfo.feeGrowthOutside0X128,\n        tickInfo.feeGrowthOutside1X128,\n        tickInfo.initialized\n      );\n    }\n    console.log(\"pool tick-level state setup successfully!\");\n\n    for (let wordPos of relatedTickBitmapWordPoses) {\n      let wordVal = await uniswapV3PoolOnMainnet.tickBitmap(wordPos);\n      await uniswapV3Pool.setTickBitmap(wordPos, wordVal);\n    }\n    console.log(\"pool tick-bitmap state setup successfully!\");\n\n    // // do swap as event id 15488\n    // let trx = await uniswapV3Pool.swap(\n    //   deployer.address,\n    //   false,\n    //   \"500000000000000000000\",\n    //   \"1461446703485210103287273052203988822378723970341\",\n    //   []\n    // );\n\n    // do swap as event id 10\n    let trx = await uniswapV3Pool.swap(\n      deployer.address,\n      false,\n      \"-184363245697871389668\",\n      // \"820850720170027688\",\n      \"4295128740\",\n      []\n    );\n\n    let receipt = await trx.wait();\n    let event = receipt.events![0].args!;\n    console.log(event);\n    console.log(event.amount0.toString());\n    console.log(event.amount1.toString());\n    console.log(event.sqrtPriceX96.toString());\n  });\n\n  function findRelatedTicks(minTick: number, maxTick: number): number[] {\n    let ticks: number[] = [];\n    let currTick = minTick;\n    while (currTick <= maxTick) {\n      ticks.push(currTick);\n      currTick += tickSpacing;\n    }\n    return ticks;\n  }\n\n  function findRelatedTickBitmapWordPoses(ticks: number[]): number[] {\n    let bitmapWordPoses: number[] = [];\n    for (let tickIndex of ticks) {\n      let bitmapWordPos = (tickIndex / tickSpacing) >> 8;\n      if (\n        bitmapWordPoses.length == 0 ||\n        bitmapWordPoses[bitmapWordPoses.length - 1] < bitmapWordPos\n      )\n        bitmapWordPoses.push(bitmapWordPos);\n    }\n    return bitmapWordPoses;\n  }\n});\n"
  },
  {
    "path": "test/contracts/Ticks.test.ts",
    "content": "import { ethers } from \"hardhat\";\nimport { expect } from \"chai\";\nimport { ConfigurableCorePool } from \"../../src/core/ConfigurableCorePool\";\nimport { SQLiteSimulationDataManager } from \"../../src/manager/SQLiteSimulationDataManager\";\nimport { SimulatorRoadmapManager } from \"../../src/manager/SimulatorRoadmapManager\";\nimport { PoolState } from \"../../src/model/PoolState\";\nimport type { UniswapV3Pool2 } from \"../typechain\";\nimport { SimulationDataManager } from \"../../src/interface/SimulationDataManager\";\nimport { SimulatorConsoleVisitor } from \"../../src/manager/SimulatorConsoleVisitor\";\nimport { SimulatorPersistenceVisitor } from \"../../src/manager/SimulatorPersistenceVisitor\";\n\ndescribe(\"Test Ticks\", function () {\n  it(\"should be identical between state of simulator implementation and mainnet state at certain block number\", async function () {\n    let simulationDataManager: SimulationDataManager =\n      await SQLiteSimulationDataManager.buildInstance(\"./test/database.db\");\n    let simulatorRoadmapManager: SimulatorRoadmapManager =\n      new SimulatorRoadmapManager(simulationDataManager);\n    let snapshot = await simulationDataManager.getSnapshot(\n      \"9577f400-5012-4492-8f1f-44c6dcb5980c\"\n      // \"f5d54d99-3148-4b9b-9661-73f3f229dce8\"\n    );\n    let testPool = new ConfigurableCorePool(\n      PoolState.from(snapshot!),\n      simulatorRoadmapManager,\n      new SimulatorConsoleVisitor(),\n      new SimulatorPersistenceVisitor(simulationDataManager)\n    );\n\n    let blockNum: number = 12464951;\n    // let blockNum: number = 12420636;\n\n    let uniswapV3PoolAddress = \"0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8\";\n    let uniswapV3Pool = (await ethers.getContractAt(\n      \"UniswapV3Pool2\",\n      uniswapV3PoolAddress\n    )) as UniswapV3Pool2;\n\n    let liquidityOnChain = await uniswapV3Pool.liquidity({\n      blockTag: blockNum,\n    });\n    let slot0OnChain = await uniswapV3Pool.slot0({\n      blockTag: blockNum,\n    });\n    let sqrtPriceOnChain = slot0OnChain.sqrtPriceX96;\n    let currentTickIndexOnChain = slot0OnChain.tick;\n    expect(liquidityOnChain.toString()).to.eql(\n      testPool.getCorePool().liquidity.toString()\n    );\n    expect(sqrtPriceOnChain.toString()).to.eql(\n      testPool.getCorePool().sqrtPriceX96.toString()\n    );\n    expect(currentTickIndexOnChain.toString()).to.eql(\n      testPool.getCorePool().tickCurrent.toString()\n    );\n\n    // count: 29475 of legal available tick index between MIN_TICK and MAX_TICK\n    function findAvailableTicks(): number[] {\n      let minTick = 199080; //-887220;\n      let maxTick = 200280; //887220;\n      let ticks: number[] = [];\n      let currTick = minTick;\n      while (currTick <= maxTick) {\n        ticks.push(currTick);\n        currTick += 60;\n      }\n      return ticks;\n    }\n\n    let availableTicks = findAvailableTicks();\n\n    for (let tickIndex of availableTicks) {\n      let tickOnChain = await uniswapV3Pool.ticks(tickIndex, {\n        blockTag: blockNum,\n      });\n      if (!tickOnChain.initialized) {\n        continue;\n      }\n      let tickInTuner = testPool.getCorePool().getTick(tickIndex);\n      expect(tickOnChain.initialized).to.be.true;\n      expect(tickOnChain.liquidityGross.toString()).to.eql(\n        tickInTuner.liquidityGross.toString()\n      );\n      expect(tickOnChain.liquidityNet.toString()).to.eql(\n        tickInTuner.liquidityNet.toString()\n      );\n      expect(tickOnChain.feeGrowthOutside0X128.toString()).to.eql(\n        tickInTuner.feeGrowthOutside0X128.toString()\n      );\n      expect(tickOnChain.feeGrowthOutside1X128.toString()).to.eql(\n        tickInTuner.feeGrowthOutside1X128.toString()\n      );\n    }\n    await simulationDataManager.close();\n  });\n});\n"
  },
  {
    "path": "test/contracts/src/contracts/NoDelegateCall.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\n/// @title Prevents delegatecall to a contract\n/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract\nabstract contract NoDelegateCall {\n    /// @dev The original address of this contract\n    address private immutable original;\n\n    constructor() {\n        // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode.\n        // In other words, this variable won't change when it's checked at runtime.\n        original = address(this);\n    }\n\n    /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method,\n    ///     and the use of immutable means the address bytes are copied in every place the modifier is used.\n    function checkNotDelegateCall() private view {\n        require(address(this) == original);\n    }\n\n    /// @notice Prevents delegatecall into the modified method\n    modifier noDelegateCall() {\n        checkNotDelegateCall();\n        _;\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/UniswapV3Factory2.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\nimport './interfaces/IUniswapV3Factory.sol';\n\nimport './UniswapV3PoolDeployer2.sol';\nimport './NoDelegateCall.sol';\n\nimport './UniswapV3Pool2.sol';\n\n/// @title Canonical Uniswap V3 factory\n/// @notice Deploys Uniswap V3 pools and manages ownership and control over pool protocol fees\ncontract UniswapV3Factory2 is IUniswapV3Factory, UniswapV3PoolDeployer2, NoDelegateCall {\n    /// @inheritdoc IUniswapV3Factory\n    address public override owner;\n\n    /// @inheritdoc IUniswapV3Factory\n    mapping(uint24 => int24) public override feeAmountTickSpacing;\n    /// @inheritdoc IUniswapV3Factory\n    mapping(address => mapping(address => mapping(uint24 => address))) public override getPool;\n\n    constructor() {\n        owner = msg.sender;\n        emit OwnerChanged(address(0), msg.sender);\n\n        feeAmountTickSpacing[500] = 10;\n        emit FeeAmountEnabled(500, 10);\n        feeAmountTickSpacing[3000] = 60;\n        emit FeeAmountEnabled(3000, 60);\n        feeAmountTickSpacing[10000] = 200;\n        emit FeeAmountEnabled(10000, 200);\n    }\n\n    /// @inheritdoc IUniswapV3Factory\n    function createPool(\n        address tokenA,\n        address tokenB,\n        uint24 fee\n    ) external override noDelegateCall returns (address pool) {\n        require(tokenA != tokenB);\n        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n        require(token0 != address(0));\n        int24 tickSpacing = feeAmountTickSpacing[fee];\n        require(tickSpacing != 0);\n        require(getPool[token0][token1][fee] == address(0));\n        pool = deploy(address(this), token0, token1, fee, tickSpacing);\n        getPool[token0][token1][fee] = pool;\n        // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses\n        getPool[token1][token0][fee] = pool;\n        emit PoolCreated(token0, token1, fee, tickSpacing, pool);\n    }\n\n    /// @inheritdoc IUniswapV3Factory\n    function setOwner(address _owner) external override {\n        require(msg.sender == owner);\n        emit OwnerChanged(owner, _owner);\n        owner = _owner;\n    }\n\n    /// @inheritdoc IUniswapV3Factory\n    function enableFeeAmount(uint24 fee, int24 tickSpacing) public override {\n        require(msg.sender == owner);\n        require(fee < 1000000);\n        // tick spacing is capped at 16384 to prevent the situation where tickSpacing is so large that\n        // TickBitmap#nextInitializedTickWithinOneWord overflows int24 container from a valid tick\n        // 16384 ticks represents a >5x price change with ticks of 1 bips\n        require(tickSpacing > 0 && tickSpacing < 16384);\n        require(feeAmountTickSpacing[fee] == 0);\n\n        feeAmountTickSpacing[fee] = tickSpacing;\n        emit FeeAmountEnabled(fee, tickSpacing);\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/UniswapV3Pool2.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\nimport './interfaces/IUniswapV3Pool.sol';\n\nimport './NoDelegateCall.sol';\n\nimport './libraries/LowGasSafeMath.sol';\nimport './libraries/SafeCast.sol';\nimport './libraries/Tick.sol';\nimport './libraries/TickBitmap.sol';\nimport './libraries/Position.sol';\nimport './libraries/Oracle.sol';\n\nimport './libraries/FullMath.sol';\nimport './libraries/FixedPoint128.sol';\nimport './libraries/TransferHelper.sol';\nimport './libraries/TickMath.sol';\nimport './libraries/LiquidityMath.sol';\nimport './libraries/SqrtPriceMath.sol';\nimport './libraries/SwapMath.sol';\n\nimport './interfaces/IUniswapV3PoolDeployer.sol';\nimport './interfaces/IUniswapV3Factory.sol';\nimport './interfaces/IERC20Minimal.sol';\nimport './interfaces/callback/IUniswapV3MintCallback.sol';\nimport './interfaces/callback/IUniswapV3SwapCallback.sol';\nimport './interfaces/callback/IUniswapV3FlashCallback.sol';\n\nimport \"hardhat/console.sol\";\n\ncontract UniswapV3Pool2 is IUniswapV3Pool, NoDelegateCall {\n    using LowGasSafeMath for uint256;\n    using LowGasSafeMath for int256;\n    using SafeCast for uint256;\n    using SafeCast for int256;\n    using Tick for mapping(int24 => Tick.Info);\n    using TickBitmap for mapping(int16 => uint256);\n    using Position for mapping(bytes32 => Position.Info);\n    using Position for Position.Info;\n    using Oracle for Oracle.Observation[65535];\n\n    /// @inheritdoc IUniswapV3PoolImmutables\n    address public immutable override factory;\n    /// @inheritdoc IUniswapV3PoolImmutables\n    address public immutable override token0;\n    /// @inheritdoc IUniswapV3PoolImmutables\n    address public immutable override token1;\n    /// @inheritdoc IUniswapV3PoolImmutables\n    uint24 public immutable override fee;\n\n    /// @inheritdoc IUniswapV3PoolImmutables\n    int24 public immutable override tickSpacing;\n\n    /// @inheritdoc IUniswapV3PoolImmutables\n    uint128 public immutable override maxLiquidityPerTick;\n\n    struct Slot0 {\n        // the current price\n        uint160 sqrtPriceX96;\n        // the current tick\n        int24 tick;\n        // the most-recently updated index of the observations array\n        uint16 observationIndex;\n        // the current maximum number of observations that are being stored\n        uint16 observationCardinality;\n        // the next maximum number of observations to store, triggered in observations.write\n        uint16 observationCardinalityNext;\n        // the current protocol fee as a percentage of the swap fee taken on withdrawal\n        // represented as an integer denominator (1/x)%\n        uint8 feeProtocol;\n        // whether the pool is locked\n        bool unlocked;\n    }\n    /// @inheritdoc IUniswapV3PoolState\n    Slot0 public override slot0;\n\n    /// @inheritdoc IUniswapV3PoolState\n    uint256 public override feeGrowthGlobal0X128;\n    /// @inheritdoc IUniswapV3PoolState\n    uint256 public override feeGrowthGlobal1X128;\n\n    // accumulated protocol fees in token0/token1 units\n    struct ProtocolFees {\n        uint128 token0;\n        uint128 token1;\n    }\n    /// @inheritdoc IUniswapV3PoolState\n    ProtocolFees public override protocolFees;\n\n    /// @inheritdoc IUniswapV3PoolState\n    uint128 public override liquidity;\n\n    /// @inheritdoc IUniswapV3PoolState\n    mapping(int24 => Tick.Info) public override ticks;\n    /// @inheritdoc IUniswapV3PoolState\n    mapping(int16 => uint256) public override tickBitmap;\n    /// @inheritdoc IUniswapV3PoolState\n    mapping(bytes32 => Position.Info) public override positions;\n    /// @inheritdoc IUniswapV3PoolState\n    Oracle.Observation[65535] public override observations;\n\n    /// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance\n    /// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because\n    /// we use balance checks to determine the payment status of interactions such as mint, swap and flash.\n    modifier lock() {\n        require(slot0.unlocked, 'LOK');\n        slot0.unlocked = false;\n        _;\n        slot0.unlocked = true;\n    }\n\n    /// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner()\n    modifier onlyFactoryOwner() {\n        require(msg.sender == IUniswapV3Factory(factory).owner());\n        _;\n    }\n\n    constructor() {\n        int24 _tickSpacing;\n        (factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters();\n        tickSpacing = _tickSpacing;\n\n        maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing);\n    }\n\n    function setState(int24 tick, uint160 sqrtPriceX96, uint128 _liquidity, uint256 _feeGrowthGlobal0X128, uint256 _feeGrowthGlobal1X128) external {\n        liquidity = _liquidity;\n        slot0.tick = tick;\n        slot0.sqrtPriceX96 = sqrtPriceX96;\n        feeGrowthGlobal0X128 = _feeGrowthGlobal0X128;\n        feeGrowthGlobal1X128 = _feeGrowthGlobal1X128;\n    }\n\n    function setTickInfo(int24 tick, uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128, bool initialized) external {\n        Tick.Info storage info = ticks[tick];\n        info.liquidityGross = liquidityGross;\n        info.liquidityNet = liquidityNet;\n        info.feeGrowthOutside0X128 = feeGrowthOutside0X128;\n        info.feeGrowthOutside1X128 = feeGrowthOutside1X128;\n        info.initialized = initialized;\n    }\n\n    function setTickBitmap(int16 wordPos, uint256 wordVal) external {\n        tickBitmap[wordPos] = wordVal;\n    }\n\n    /// @dev Common checks for valid tick inputs.\n    function checkTicks(int24 tickLower, int24 tickUpper) private pure {\n        require(tickLower < tickUpper, 'TLU');\n        require(tickLower >= TickMath.MIN_TICK, 'TLM');\n        require(tickUpper <= TickMath.MAX_TICK, 'TUM');\n    }\n\n    /// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests.\n    function _blockTimestamp() internal view virtual returns (uint32) {\n        return uint32(block.timestamp); // truncation is desired\n    }\n\n    /// @dev Get the pool's balance of token0\n    /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize\n    /// check\n    function balance0() private view returns (uint256) {\n        (bool success, bytes memory data) =\n            token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));\n        require(success && data.length >= 32);\n        return abi.decode(data, (uint256));\n    }\n\n    /// @dev Get the pool's balance of token1\n    /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize\n    /// check\n    function balance1() private view returns (uint256) {\n        (bool success, bytes memory data) =\n            token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));\n        require(success && data.length >= 32);\n        return abi.decode(data, (uint256));\n    }\n\n    /// @inheritdoc IUniswapV3PoolDerivedState\n    function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)\n        external\n        view\n        override\n        noDelegateCall\n        returns (\n            int56 tickCumulativeInside,\n            uint160 secondsPerLiquidityInsideX128,\n            uint32 secondsInside\n        )\n    {\n        checkTicks(tickLower, tickUpper);\n\n        int56 tickCumulativeLower;\n        int56 tickCumulativeUpper;\n        uint160 secondsPerLiquidityOutsideLowerX128;\n        uint160 secondsPerLiquidityOutsideUpperX128;\n        uint32 secondsOutsideLower;\n        uint32 secondsOutsideUpper;\n\n        {\n            Tick.Info storage lower = ticks[tickLower];\n            Tick.Info storage upper = ticks[tickUpper];\n            bool initializedLower;\n            (tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = (\n                lower.tickCumulativeOutside,\n                lower.secondsPerLiquidityOutsideX128,\n                lower.secondsOutside,\n                lower.initialized\n            );\n            require(initializedLower);\n\n            bool initializedUpper;\n            (tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = (\n                upper.tickCumulativeOutside,\n                upper.secondsPerLiquidityOutsideX128,\n                upper.secondsOutside,\n                upper.initialized\n            );\n            require(initializedUpper);\n        }\n\n        Slot0 memory _slot0 = slot0;\n\n        if (_slot0.tick < tickLower) {\n            return (\n                tickCumulativeLower - tickCumulativeUpper,\n                secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128,\n                secondsOutsideLower - secondsOutsideUpper\n            );\n        } else if (_slot0.tick < tickUpper) {\n            uint32 time = _blockTimestamp();\n            (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =\n                observations.observeSingle(\n                    time,\n                    0,\n                    _slot0.tick,\n                    _slot0.observationIndex,\n                    liquidity,\n                    _slot0.observationCardinality\n                );\n            return (\n                tickCumulative - tickCumulativeLower - tickCumulativeUpper,\n                secondsPerLiquidityCumulativeX128 -\n                    secondsPerLiquidityOutsideLowerX128 -\n                    secondsPerLiquidityOutsideUpperX128,\n                time - secondsOutsideLower - secondsOutsideUpper\n            );\n        } else {\n            return (\n                tickCumulativeUpper - tickCumulativeLower,\n                secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128,\n                secondsOutsideUpper - secondsOutsideLower\n            );\n        }\n    }\n\n    /// @inheritdoc IUniswapV3PoolDerivedState\n    function observe(uint32[] calldata secondsAgos)\n        external\n        view\n        override\n        noDelegateCall\n        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)\n    {\n        return\n            observations.observe(\n                _blockTimestamp(),\n                secondsAgos,\n                slot0.tick,\n                slot0.observationIndex,\n                liquidity,\n                slot0.observationCardinality\n            );\n    }\n\n    /// @inheritdoc IUniswapV3PoolActions\n    function increaseObservationCardinalityNext(uint16 observationCardinalityNext)\n        external\n        override\n        lock\n        noDelegateCall\n    {\n        uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event\n        uint16 observationCardinalityNextNew =\n            observations.grow(observationCardinalityNextOld, observationCardinalityNext);\n        slot0.observationCardinalityNext = observationCardinalityNextNew;\n        if (observationCardinalityNextOld != observationCardinalityNextNew)\n            emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);\n    }\n\n    /// @inheritdoc IUniswapV3PoolActions\n    /// @dev not locked because it initializes unlocked\n    function initialize(uint160 sqrtPriceX96) external override {\n        require(slot0.sqrtPriceX96 == 0, 'AI');\n\n        int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);\n\n        (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());\n\n        slot0 = Slot0({\n            sqrtPriceX96: sqrtPriceX96,\n            tick: tick,\n            observationIndex: 0,\n            observationCardinality: cardinality,\n            observationCardinalityNext: cardinalityNext,\n            feeProtocol: 0,\n            unlocked: true\n        });\n\n        emit Initialize(sqrtPriceX96, tick);\n    }\n\n    struct ModifyPositionParams {\n        // the address that owns the position\n        address owner;\n        // the lower and upper tick of the position\n        int24 tickLower;\n        int24 tickUpper;\n        // any change in liquidity\n        int128 liquidityDelta;\n    }\n\n    /// @dev Effect some changes to a position\n    /// @param params the position details and the change to the position's liquidity to effect\n    /// @return position a storage pointer referencing the position with the given owner and tick range\n    /// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient\n    /// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient\n    function _modifyPosition(ModifyPositionParams memory params)\n        private\n        noDelegateCall\n        returns (\n            Position.Info storage position,\n            int256 amount0,\n            int256 amount1\n        )\n    {\n        checkTicks(params.tickLower, params.tickUpper);\n\n        Slot0 memory _slot0 = slot0; // SLOAD for gas optimization\n\n        position = _updatePosition(\n            params.owner,\n            params.tickLower,\n            params.tickUpper,\n            params.liquidityDelta,\n            _slot0.tick\n        );\n\n        if (params.liquidityDelta != 0) {\n            if (_slot0.tick < params.tickLower) {\n                // current tick is below the passed range; liquidity can only become in range by crossing from left to\n                // right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it\n                amount0 = SqrtPriceMath.getAmount0Delta(\n                    TickMath.getSqrtRatioAtTick(params.tickLower),\n                    TickMath.getSqrtRatioAtTick(params.tickUpper),\n                    params.liquidityDelta\n                );\n            } else if (_slot0.tick < params.tickUpper) {\n                // current tick is inside the passed range\n                uint128 liquidityBefore = liquidity; // SLOAD for gas optimization\n\n                // write an oracle entry\n                (slot0.observationIndex, slot0.observationCardinality) = observations.write(\n                    _slot0.observationIndex,\n                    _blockTimestamp(),\n                    _slot0.tick,\n                    liquidityBefore,\n                    _slot0.observationCardinality,\n                    _slot0.observationCardinalityNext\n                );\n\n                amount0 = SqrtPriceMath.getAmount0Delta(\n                    _slot0.sqrtPriceX96,\n                    TickMath.getSqrtRatioAtTick(params.tickUpper),\n                    params.liquidityDelta\n                );\n                amount1 = SqrtPriceMath.getAmount1Delta(\n                    TickMath.getSqrtRatioAtTick(params.tickLower),\n                    _slot0.sqrtPriceX96,\n                    params.liquidityDelta\n                );\n\n                liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);\n            } else {\n                // current tick is above the passed range; liquidity can only become in range by crossing from right to\n                // left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it\n                amount1 = SqrtPriceMath.getAmount1Delta(\n                    TickMath.getSqrtRatioAtTick(params.tickLower),\n                    TickMath.getSqrtRatioAtTick(params.tickUpper),\n                    params.liquidityDelta\n                );\n            }\n        }\n    }\n\n    /// @dev Gets and updates a position with the given liquidity delta\n    /// @param owner the owner of the position\n    /// @param tickLower the lower tick of the position's tick range\n    /// @param tickUpper the upper tick of the position's tick range\n    /// @param tick the current tick, passed to avoid sloads\n    function _updatePosition(\n        address owner,\n        int24 tickLower,\n        int24 tickUpper,\n        int128 liquidityDelta,\n        int24 tick\n    ) private returns (Position.Info storage position) {\n        position = positions.get(owner, tickLower, tickUpper);\n\n        uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization\n        uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization\n\n        // if we need to update the ticks, do it\n        bool flippedLower;\n        bool flippedUpper;\n        if (liquidityDelta != 0) {\n            uint32 time = _blockTimestamp();\n            (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =\n                observations.observeSingle(\n                    time,\n                    0,\n                    slot0.tick,\n                    slot0.observationIndex,\n                    liquidity,\n                    slot0.observationCardinality\n                );\n\n            flippedLower = ticks.update(\n                tickLower,\n                tick,\n                liquidityDelta,\n                _feeGrowthGlobal0X128,\n                _feeGrowthGlobal1X128,\n                secondsPerLiquidityCumulativeX128,\n                tickCumulative,\n                time,\n                false,\n                maxLiquidityPerTick\n            );\n            flippedUpper = ticks.update(\n                tickUpper,\n                tick,\n                liquidityDelta,\n                _feeGrowthGlobal0X128,\n                _feeGrowthGlobal1X128,\n                secondsPerLiquidityCumulativeX128,\n                tickCumulative,\n                time,\n                true,\n                maxLiquidityPerTick\n            );\n\n            if (flippedLower) {\n                tickBitmap.flipTick(tickLower, tickSpacing);\n            }\n            if (flippedUpper) {\n                tickBitmap.flipTick(tickUpper, tickSpacing);\n            }\n        }\n\n        (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =\n            ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128);\n\n        position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);\n\n        // clear any tick data that is no longer needed\n        if (liquidityDelta < 0) {\n            if (flippedLower) {\n                ticks.clear(tickLower);\n            }\n            if (flippedUpper) {\n                ticks.clear(tickUpper);\n            }\n        }\n    }\n\n    /// @inheritdoc IUniswapV3PoolActions\n    /// @dev noDelegateCall is applied indirectly via _modifyPosition\n    function mint(\n        address recipient,\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount,\n        bytes calldata data\n    ) external override lock returns (uint256 amount0, uint256 amount1) {\n        require(amount > 0);\n        (, int256 amount0Int, int256 amount1Int) =\n            _modifyPosition(\n                ModifyPositionParams({\n                    owner: recipient,\n                    tickLower: tickLower,\n                    tickUpper: tickUpper,\n                    liquidityDelta: int256(amount).toInt128()\n                })\n            );\n\n        amount0 = uint256(amount0Int);\n        amount1 = uint256(amount1Int);\n\n        uint256 balance0Before;\n        uint256 balance1Before;\n        if (amount0 > 0) balance0Before = balance0();\n        if (amount1 > 0) balance1Before = balance1();\n        IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);\n        if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');\n        if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');\n\n        emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);\n    }\n\n    /// @inheritdoc IUniswapV3PoolActions\n    function collect(\n        address recipient,\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount0Requested,\n        uint128 amount1Requested\n    ) external override lock returns (uint128 amount0, uint128 amount1) {\n        // we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1}\n        Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);\n\n        amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;\n        amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;\n\n        if (amount0 > 0) {\n            position.tokensOwed0 -= amount0;\n            TransferHelper.safeTransfer(token0, recipient, amount0);\n        }\n        if (amount1 > 0) {\n            position.tokensOwed1 -= amount1;\n            TransferHelper.safeTransfer(token1, recipient, amount1);\n        }\n\n        emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);\n    }\n\n    /// @inheritdoc IUniswapV3PoolActions\n    /// @dev noDelegateCall is applied indirectly via _modifyPosition\n    function burn(\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount\n    ) external override lock returns (uint256 amount0, uint256 amount1) {\n        (Position.Info storage position, int256 amount0Int, int256 amount1Int) =\n            _modifyPosition(\n                ModifyPositionParams({\n                    owner: msg.sender,\n                    tickLower: tickLower,\n                    tickUpper: tickUpper,\n                    liquidityDelta: -int256(amount).toInt128()\n                })\n            );\n\n        amount0 = uint256(-amount0Int);\n        amount1 = uint256(-amount1Int);\n\n        if (amount0 > 0 || amount1 > 0) {\n            (position.tokensOwed0, position.tokensOwed1) = (\n                position.tokensOwed0 + uint128(amount0),\n                position.tokensOwed1 + uint128(amount1)\n            );\n        }\n\n        emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);\n    }\n\n    struct SwapCache {\n        // the protocol fee for the input token\n        uint8 feeProtocol;\n        // liquidity at the beginning of the swap\n        uint128 liquidityStart;\n        // the timestamp of the current block\n        uint32 blockTimestamp;\n        // the current value of the tick accumulator, computed only if we cross an initialized tick\n        int56 tickCumulative;\n        // the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick\n        uint160 secondsPerLiquidityCumulativeX128;\n        // whether we've computed and cached the above two accumulators\n        bool computedLatestObservation;\n    }\n\n    // the top level state of the swap, the results of which are recorded in storage at the end\n    struct SwapState {\n        // the amount remaining to be swapped in/out of the input/output asset\n        int256 amountSpecifiedRemaining;\n        // the amount already swapped out/in of the output/input asset\n        int256 amountCalculated;\n        // current sqrt(price)\n        uint160 sqrtPriceX96;\n        // the tick associated with the current price\n        int24 tick;\n        // the global fee growth of the input token\n        uint256 feeGrowthGlobalX128;\n        // amount of input token paid as protocol fee\n        uint128 protocolFee;\n        // the current liquidity in range\n        uint128 liquidity;\n    }\n\n    struct StepComputations {\n        // the price at the beginning of the step\n        uint160 sqrtPriceStartX96;\n        // the next tick to swap to from the current tick in the swap direction\n        int24 tickNext;\n        // whether tickNext is initialized or not\n        bool initialized;\n        // sqrt(price) for the next tick (1/0)\n        uint160 sqrtPriceNextX96;\n        // how much is being swapped in in this step\n        uint256 amountIn;\n        // how much is being swapped out\n        uint256 amountOut;\n        // how much fee is being paid in\n        uint256 feeAmount;\n    }\n\n    /// @inheritdoc IUniswapV3PoolActions\n    function swap(\n        address recipient,\n        bool zeroForOne,\n        int256 amountSpecified,\n        uint160 sqrtPriceLimitX96,\n        bytes calldata data\n    ) external override noDelegateCall returns (int256 amount0, int256 amount1) {\n        require(amountSpecified != 0, 'AS');\n\n        Slot0 memory slot0Start = slot0;\n\n        require(slot0Start.unlocked, 'LOK');\n        require(\n            zeroForOne\n                ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO\n                : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,\n            'SPL'\n        );\n\n        slot0.unlocked = false;\n\n        SwapCache memory cache =\n            SwapCache({\n                liquidityStart: liquidity,\n                blockTimestamp: _blockTimestamp(),\n                feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4),\n                secondsPerLiquidityCumulativeX128: 0,\n                tickCumulative: 0,\n                computedLatestObservation: false\n            });\n\n        bool exactInput = amountSpecified > 0;\n\n        SwapState memory state =\n            SwapState({\n                amountSpecifiedRemaining: amountSpecified,\n                amountCalculated: 0,\n                sqrtPriceX96: slot0Start.sqrtPriceX96,\n                tick: slot0Start.tick,\n                feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,\n                protocolFee: 0,\n                liquidity: cache.liquidityStart\n            });\n\n        // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit\n        while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {\n            StepComputations memory step;\n\n            step.sqrtPriceStartX96 = state.sqrtPriceX96;\n\n            (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(\n                state.tick,\n                tickSpacing,\n                zeroForOne\n            );\n\n            // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds\n            if (step.tickNext < TickMath.MIN_TICK) {\n                step.tickNext = TickMath.MIN_TICK;\n            } else if (step.tickNext > TickMath.MAX_TICK) {\n                step.tickNext = TickMath.MAX_TICK;\n            }\n\n            // get the price for the next tick\n            step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);\n\n            // compute values to swap to the target tick, price limit, or point where input/output amount is exhausted\n            (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(\n                state.sqrtPriceX96,\n                (zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)\n                    ? sqrtPriceLimitX96\n                    : step.sqrtPriceNextX96,\n                state.liquidity,\n                state.amountSpecifiedRemaining,\n                fee\n            );\n\n            if (exactInput) {\n                state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256();\n                state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256());\n            } else {\n                state.amountSpecifiedRemaining += step.amountOut.toInt256();\n                state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256());\n            }\n\n            // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee\n            if (cache.feeProtocol > 0) {\n                uint256 delta = step.feeAmount / cache.feeProtocol;\n                step.feeAmount -= delta;\n                state.protocolFee += uint128(delta);\n            }\n\n            // update global fee tracker\n            if (state.liquidity > 0)\n                state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity);\n\n            // shift tick if we reached the next price\n            if (state.sqrtPriceX96 == step.sqrtPriceNextX96) {\n                // if the tick is initialized, run the tick transition\n                if (step.initialized) {\n                    // check for the placeholder value, which we replace with the actual value the first time the swap\n                    // crosses an initialized tick\n                    // if (!cache.computedLatestObservation) {\n                    //     (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle(\n                    //         cache.blockTimestamp,\n                    //         0,\n                    //         slot0Start.tick,\n                    //         slot0Start.observationIndex,\n                    //         cache.liquidityStart,\n                    //         slot0Start.observationCardinality\n                    //     );\n                    //     cache.computedLatestObservation = true;\n                    // }\n                    int128 liquidityNet =\n                        ticks.cross(\n                            step.tickNext,\n                            (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128),\n                            (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128),\n                            cache.secondsPerLiquidityCumulativeX128,\n                            cache.tickCumulative,\n                            cache.blockTimestamp\n                        );\n                    // if we're moving leftward, we interpret liquidityNet as the opposite sign\n                    // safe because liquidityNet cannot be type(int128).min\n                    if (zeroForOne) liquidityNet = -liquidityNet;\n\n                    state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet);\n                }\n\n                state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;\n            } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) {\n                // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved\n                state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);\n            }\n        }\n\n        // update tick and write an oracle entry if the tick change\n        if (state.tick != slot0Start.tick) {\n            (uint16 observationIndex, uint16 observationCardinality) =\n                observations.write(\n                    slot0Start.observationIndex,\n                    cache.blockTimestamp,\n                    slot0Start.tick,\n                    cache.liquidityStart,\n                    slot0Start.observationCardinality,\n                    slot0Start.observationCardinalityNext\n                );\n            (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = (\n                state.sqrtPriceX96,\n                state.tick,\n                observationIndex,\n                observationCardinality\n            );\n        } else {\n            // otherwise just update the price\n            slot0.sqrtPriceX96 = state.sqrtPriceX96;\n        }\n\n        // update liquidity if it changed\n        if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity;\n\n        // update fee growth global and, if necessary, protocol fees\n        // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees\n        if (zeroForOne) {\n            feeGrowthGlobal0X128 = state.feeGrowthGlobalX128;\n            if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee;\n        } else {\n            feeGrowthGlobal1X128 = state.feeGrowthGlobalX128;\n            if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee;\n        }\n\n        (amount0, amount1) = zeroForOne == exactInput\n            ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated)\n            : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining);\n\n        // do the transfers and collect payment\n        // if (zeroForOne) {\n        //     if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1));\n\n        //     uint256 balance0Before = balance0();\n        //     IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data);\n        //     require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA');\n        // } else {\n        //     if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0));\n\n        //     uint256 balance1Before = balance1();\n        //     IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data);\n        //     require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA');\n        // }\n\n        emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick);\n        slot0.unlocked = true;\n    }\n\n    /// @inheritdoc IUniswapV3PoolActions\n    function flash(\n        address recipient,\n        uint256 amount0,\n        uint256 amount1,\n        bytes calldata data\n    ) external override lock noDelegateCall {\n        uint128 _liquidity = liquidity;\n        require(_liquidity > 0, 'L');\n\n        uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6);\n        uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6);\n        uint256 balance0Before = balance0();\n        uint256 balance1Before = balance1();\n\n        if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0);\n        if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1);\n\n        IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data);\n\n        uint256 balance0After = balance0();\n        uint256 balance1After = balance1();\n\n        require(balance0Before.add(fee0) <= balance0After, 'F0');\n        require(balance1Before.add(fee1) <= balance1After, 'F1');\n\n        // sub is safe because we know balanceAfter is gt balanceBefore by at least fee\n        uint256 paid0 = balance0After - balance0Before;\n        uint256 paid1 = balance1After - balance1Before;\n\n        if (paid0 > 0) {\n            uint8 feeProtocol0 = slot0.feeProtocol % 16;\n            uint256 fees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0;\n            if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0);\n            feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity);\n        }\n        if (paid1 > 0) {\n            uint8 feeProtocol1 = slot0.feeProtocol >> 4;\n            uint256 fees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1;\n            if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1);\n            feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity);\n        }\n\n        emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1);\n    }\n\n    /// @inheritdoc IUniswapV3PoolOwnerActions\n    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner {\n        require(\n            (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) &&\n                (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10))\n        );\n        uint8 feeProtocolOld = slot0.feeProtocol;\n        slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4);\n        emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1);\n    }\n\n    /// @inheritdoc IUniswapV3PoolOwnerActions\n    function collectProtocol(\n        address recipient,\n        uint128 amount0Requested,\n        uint128 amount1Requested\n    ) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) {\n        amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested;\n        amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested;\n\n        if (amount0 > 0) {\n            if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings\n            protocolFees.token0 -= amount0;\n            TransferHelper.safeTransfer(token0, recipient, amount0);\n        }\n        if (amount1 > 0) {\n            if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings\n            protocolFees.token1 -= amount1;\n            TransferHelper.safeTransfer(token1, recipient, amount1);\n        }\n\n        emit CollectProtocol(msg.sender, recipient, amount0, amount1);\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/UniswapV3PoolDeployer2.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\nimport './interfaces/IUniswapV3PoolDeployer.sol';\n\nimport './UniswapV3Pool2.sol';\n\ncontract UniswapV3PoolDeployer2 is IUniswapV3PoolDeployer {\n    struct Parameters {\n        address factory;\n        address token0;\n        address token1;\n        uint24 fee;\n        int24 tickSpacing;\n    }\n\n    /// @inheritdoc IUniswapV3PoolDeployer\n    Parameters public override parameters;\n\n    /// @dev Deploys a pool with the given parameters by transiently setting the parameters storage slot and then\n    /// clearing it after deploying the pool.\n    /// @param factory The contract address of the Uniswap V3 factory\n    /// @param token0 The first token of the pool by address sort order\n    /// @param token1 The second token of the pool by address sort order\n    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip\n    /// @param tickSpacing The spacing between usable ticks\n    function deploy(\n        address factory,\n        address token0,\n        address token1,\n        uint24 fee,\n        int24 tickSpacing\n    ) internal returns (address pool) {\n        parameters = Parameters({factory: factory, token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing});\n        pool = address(new UniswapV3Pool2{salt: keccak256(abi.encode(token0, token1, fee))}());\n        delete parameters;\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/IERC20Minimal.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Minimal ERC20 interface for Uniswap\n/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3\ninterface IERC20Minimal {\n    /// @notice Returns the balance of a token\n    /// @param account The account for which to look up the number of tokens it has, i.e. its balance\n    /// @return The number of tokens held by the account\n    function balanceOf(address account) external view returns (uint256);\n\n    /// @notice Transfers the amount of token from the `msg.sender` to the recipient\n    /// @param recipient The account that will receive the amount transferred\n    /// @param amount The number of tokens to send from the sender to the recipient\n    /// @return Returns true for a successful transfer, false for an unsuccessful transfer\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /// @notice Returns the current allowance given to a spender by an owner\n    /// @param owner The account of the token owner\n    /// @param spender The account of the token spender\n    /// @return The current allowance granted by `owner` to `spender`\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`\n    /// @param spender The account which will be allowed to spend a given amount of the owners tokens\n    /// @param amount The amount of tokens allowed to be used by `spender`\n    /// @return Returns true for a successful approval, false for unsuccessful\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`\n    /// @param sender The account from which the transfer will be initiated\n    /// @param recipient The recipient of the transfer\n    /// @param amount The amount of the transfer\n    /// @return Returns true for a successful transfer, false for unsuccessful\n    function transferFrom(\n        address sender,\n        address recipient,\n        uint256 amount\n    ) external returns (bool);\n\n    /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.\n    /// @param from The account from which the tokens were sent, i.e. the balance decreased\n    /// @param to The account to which the tokens were sent, i.e. the balance increased\n    /// @param value The amount of tokens that were transferred\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.\n    /// @param owner The account that approved spending of its tokens\n    /// @param spender The account for which the spending allowance was modified\n    /// @param value The new allowance from the owner to the spender\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/IUniswapV3Factory.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title The interface for the Uniswap V3 Factory\n/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees\ninterface IUniswapV3Factory {\n    /// @notice Emitted when the owner of the factory is changed\n    /// @param oldOwner The owner before the owner was changed\n    /// @param newOwner The owner after the owner was changed\n    event OwnerChanged(address indexed oldOwner, address indexed newOwner);\n\n    /// @notice Emitted when a pool is created\n    /// @param token0 The first token of the pool by address sort order\n    /// @param token1 The second token of the pool by address sort order\n    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip\n    /// @param tickSpacing The minimum number of ticks between initialized ticks\n    /// @param pool The address of the created pool\n    event PoolCreated(\n        address indexed token0,\n        address indexed token1,\n        uint24 indexed fee,\n        int24 tickSpacing,\n        address pool\n    );\n\n    /// @notice Emitted when a new fee amount is enabled for pool creation via the factory\n    /// @param fee The enabled fee, denominated in hundredths of a bip\n    /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee\n    event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);\n\n    /// @notice Returns the current owner of the factory\n    /// @dev Can be changed by the current owner via setOwner\n    /// @return The address of the factory owner\n    function owner() external view returns (address);\n\n    /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled\n    /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context\n    /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee\n    /// @return The tick spacing\n    function feeAmountTickSpacing(uint24 fee) external view returns (int24);\n\n    /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist\n    /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order\n    /// @param tokenA The contract address of either token0 or token1\n    /// @param tokenB The contract address of the other token\n    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip\n    /// @return pool The pool address\n    function getPool(\n        address tokenA,\n        address tokenB,\n        uint24 fee\n    ) external view returns (address pool);\n\n    /// @notice Creates a pool for the given two tokens and fee\n    /// @param tokenA One of the two tokens in the desired pool\n    /// @param tokenB The other of the two tokens in the desired pool\n    /// @param fee The desired fee for the pool\n    /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved\n    /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments\n    /// are invalid.\n    /// @return pool The address of the newly created pool\n    function createPool(\n        address tokenA,\n        address tokenB,\n        uint24 fee\n    ) external returns (address pool);\n\n    /// @notice Updates the owner of the factory\n    /// @dev Must be called by the current owner\n    /// @param _owner The new owner of the factory\n    function setOwner(address _owner) external;\n\n    /// @notice Enables a fee amount with the given tickSpacing\n    /// @dev Fee amounts may never be removed once enabled\n    /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)\n    /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount\n    function enableFeeAmount(uint24 fee, int24 tickSpacing) external;\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/IUniswapV3Pool.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\nimport './pool/IUniswapV3PoolImmutables.sol';\nimport './pool/IUniswapV3PoolState.sol';\nimport './pool/IUniswapV3PoolDerivedState.sol';\nimport './pool/IUniswapV3PoolActions.sol';\nimport './pool/IUniswapV3PoolOwnerActions.sol';\nimport './pool/IUniswapV3PoolEvents.sol';\n\n/// @title The interface for a Uniswap V3 Pool\n/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform\n/// to the ERC20 specification\n/// @dev The pool interface is broken up into many smaller pieces\ninterface IUniswapV3Pool is\n    IUniswapV3PoolImmutables,\n    IUniswapV3PoolState,\n    IUniswapV3PoolDerivedState,\n    IUniswapV3PoolActions,\n    IUniswapV3PoolOwnerActions,\n    IUniswapV3PoolEvents\n{\n\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/IUniswapV3PoolDeployer.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools\n/// @notice A contract that constructs a pool must implement this to pass arguments to the pool\n/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash\n/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain\ninterface IUniswapV3PoolDeployer {\n    /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation.\n    /// @dev Called by the pool constructor to fetch the parameters of the pool\n    /// Returns factory The factory address\n    /// Returns token0 The first token of the pool by address sort order\n    /// Returns token1 The second token of the pool by address sort order\n    /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip\n    /// Returns tickSpacing The minimum number of ticks between initialized ticks\n    function parameters()\n        external\n        view\n        returns (\n            address factory,\n            address token0,\n            address token1,\n            uint24 fee,\n            int24 tickSpacing\n        );\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License."
  },
  {
    "path": "test/contracts/src/contracts/interfaces/callback/IUniswapV3FlashCallback.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Callback for IUniswapV3PoolActions#flash\n/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface\ninterface IUniswapV3FlashCallback {\n    /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash.\n    /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts.\n    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n    /// @param fee0 The fee amount in token0 due to the pool by the end of the flash\n    /// @param fee1 The fee amount in token1 due to the pool by the end of the flash\n    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call\n    function uniswapV3FlashCallback(\n        uint256 fee0,\n        uint256 fee1,\n        bytes calldata data\n    ) external;\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/callback/IUniswapV3MintCallback.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Callback for IUniswapV3PoolActions#mint\n/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface\ninterface IUniswapV3MintCallback {\n    /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint.\n    /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity.\n    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n    /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity\n    /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity\n    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call\n    function uniswapV3MintCallback(\n        uint256 amount0Owed,\n        uint256 amount1Owed,\n        bytes calldata data\n    ) external;\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/callback/IUniswapV3SwapCallback.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Callback for IUniswapV3PoolActions#swap\n/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface\ninterface IUniswapV3SwapCallback {\n    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n    /// @dev In the implementation you must pay the pool tokens owed for the swap.\n    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n    function uniswapV3SwapCallback(\n        int256 amount0Delta,\n        int256 amount1Delta,\n        bytes calldata data\n    ) external;\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolActions.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Permissionless pool actions\n/// @notice Contains pool methods that can be called by anyone\ninterface IUniswapV3PoolActions {\n    /// @notice Sets the initial price for the pool\n    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value\n    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96\n    function initialize(uint160 sqrtPriceX96) external;\n\n    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position\n    /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback\n    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends\n    /// on tickLower, tickUpper, the amount of liquidity, and the current price.\n    /// @param recipient The address for which the liquidity will be created\n    /// @param tickLower The lower tick of the position in which to add liquidity\n    /// @param tickUpper The upper tick of the position in which to add liquidity\n    /// @param amount The amount of liquidity to mint\n    /// @param data Any data that should be passed through to the callback\n    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback\n    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback\n    function mint(\n        address recipient,\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount,\n        bytes calldata data\n    ) external returns (uint256 amount0, uint256 amount1);\n\n    /// @notice Collects tokens owed to a position\n    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.\n    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or\n    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the\n    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.\n    /// @param recipient The address which should receive the fees collected\n    /// @param tickLower The lower tick of the position for which to collect fees\n    /// @param tickUpper The upper tick of the position for which to collect fees\n    /// @param amount0Requested How much token0 should be withdrawn from the fees owed\n    /// @param amount1Requested How much token1 should be withdrawn from the fees owed\n    /// @return amount0 The amount of fees collected in token0\n    /// @return amount1 The amount of fees collected in token1\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\n    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position\n    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0\n    /// @dev Fees must be collected separately via a call to #collect\n    /// @param tickLower The lower tick of the position for which to burn liquidity\n    /// @param tickUpper The upper tick of the position for which to burn liquidity\n    /// @param amount How much liquidity to burn\n    /// @return amount0 The amount of token0 sent to the recipient\n    /// @return amount1 The amount of token1 sent to the recipient\n    function burn(\n        int24 tickLower,\n        int24 tickUpper,\n        uint128 amount\n    ) external returns (uint256 amount0, uint256 amount1);\n\n    /// @notice Swap token0 for token1, or token1 for token0\n    /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback\n    /// @param recipient The address to receive the output of the swap\n    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0\n    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)\n    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this\n    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap\n    /// @param data Any data to be passed through to the callback\n    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive\n    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive\n    function swap(\n        address recipient,\n        bool zeroForOne,\n        int256 amountSpecified,\n        uint160 sqrtPriceLimitX96,\n        bytes calldata data\n    ) external returns (int256 amount0, int256 amount1);\n\n    /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback\n    /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback\n    /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling\n    /// with 0 amount{0,1} and sending the donation amount(s) from the callback\n    /// @param recipient The address which will receive the token0 and token1 amounts\n    /// @param amount0 The amount of token0 to send\n    /// @param amount1 The amount of token1 to send\n    /// @param data Any data to be passed through to the callback\n    function flash(\n        address recipient,\n        uint256 amount0,\n        uint256 amount1,\n        bytes calldata data\n    ) external;\n\n    /// @notice Increase the maximum number of price and liquidity observations that this pool will store\n    /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to\n    /// the input observationCardinalityNext.\n    /// @param observationCardinalityNext The desired minimum number of observations for the pool to store\n    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that is not stored\n/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the\n/// blockchain. The functions here may have variable gas costs.\ninterface IUniswapV3PoolDerivedState {\n    /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp\n    /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing\n    /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,\n    /// you must call it with secondsAgos = [3600, 0].\n    /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in\n    /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.\n    /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned\n    /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp\n    /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block\n    /// timestamp\n    function observe(uint32[] calldata secondsAgos)\n        external\n        view\n        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);\n\n    /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range\n    /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.\n    /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first\n    /// snapshot is taken and the second snapshot is taken.\n    /// @param tickLower The lower tick of the range\n    /// @param tickUpper The upper tick of the range\n    /// @return tickCumulativeInside The snapshot of the tick accumulator for the range\n    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range\n    /// @return secondsInside The snapshot of seconds per liquidity for the range\n    function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)\n        external\n        view\n        returns (\n            int56 tickCumulativeInside,\n            uint160 secondsPerLiquidityInsideX128,\n            uint32 secondsInside\n        );\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolEvents.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Events emitted by a pool\n/// @notice Contains all events emitted by the pool\ninterface IUniswapV3PoolEvents {\n    /// @notice Emitted exactly once by a pool when #initialize is first called on the pool\n    /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize\n    /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96\n    /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool\n    event Initialize(uint160 sqrtPriceX96, int24 tick);\n\n    /// @notice Emitted when liquidity is minted for a given position\n    /// @param sender The address that minted the liquidity\n    /// @param owner The owner of the position and recipient of any minted liquidity\n    /// @param tickLower The lower tick of the position\n    /// @param tickUpper The upper tick of the position\n    /// @param amount The amount of liquidity minted to the position range\n    /// @param amount0 How much token0 was required for the minted liquidity\n    /// @param amount1 How much token1 was required for the minted liquidity\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\n    /// @notice Emitted when fees are collected by the owner of a position\n    /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees\n    /// @param owner The owner of the position for which fees are collected\n    /// @param tickLower The lower tick of the position\n    /// @param tickUpper The upper tick of the position\n    /// @param amount0 The amount of token0 fees collected\n    /// @param amount1 The amount of token1 fees collected\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\n    /// @notice Emitted when a position's liquidity is removed\n    /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect\n    /// @param owner The owner of the position for which liquidity is removed\n    /// @param tickLower The lower tick of the position\n    /// @param tickUpper The upper tick of the position\n    /// @param amount The amount of liquidity to remove\n    /// @param amount0 The amount of token0 withdrawn\n    /// @param amount1 The amount of token1 withdrawn\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\n    /// @notice Emitted by the pool for any swaps between token0 and token1\n    /// @param sender The address that initiated the swap call, and that received the callback\n    /// @param recipient The address that received the output of the swap\n    /// @param amount0 The delta of the token0 balance of the pool\n    /// @param amount1 The delta of the token1 balance of the pool\n    /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96\n    /// @param liquidity The liquidity of the pool after the swap\n    /// @param tick The log base 1.0001 of price of the pool after the swap\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    /// @notice Emitted by the pool for any flashes of token0/token1\n    /// @param sender The address that initiated the swap call, and that received the callback\n    /// @param recipient The address that received the tokens from flash\n    /// @param amount0 The amount of token0 that was flashed\n    /// @param amount1 The amount of token1 that was flashed\n    /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee\n    /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee\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\n    /// @notice Emitted by the pool for increases to the number of observations that can be stored\n    /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index\n    /// just before a mint/swap/burn.\n    /// @param observationCardinalityNextOld The previous value of the next observation cardinality\n    /// @param observationCardinalityNextNew The updated value of the next observation cardinality\n    event IncreaseObservationCardinalityNext(\n        uint16 observationCardinalityNextOld,\n        uint16 observationCardinalityNextNew\n    );\n\n    /// @notice Emitted when the protocol fee is changed by the pool\n    /// @param feeProtocol0Old The previous value of the token0 protocol fee\n    /// @param feeProtocol1Old The previous value of the token1 protocol fee\n    /// @param feeProtocol0New The updated value of the token0 protocol fee\n    /// @param feeProtocol1New The updated value of the token1 protocol fee\n    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);\n\n    /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner\n    /// @param sender The address that collects the protocol fees\n    /// @param recipient The address that receives the collected protocol fees\n    /// @param amount0 The amount of token0 protocol fees that is withdrawn\n    /// @param amount0 The amount of token1 protocol fees that is withdrawn\n    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that never changes\n/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values\ninterface IUniswapV3PoolImmutables {\n    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface\n    /// @return The contract address\n    function factory() external view returns (address);\n\n    /// @notice The first of the two tokens of the pool, sorted by address\n    /// @return The token contract address\n    function token0() external view returns (address);\n\n    /// @notice The second of the two tokens of the pool, sorted by address\n    /// @return The token contract address\n    function token1() external view returns (address);\n\n    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6\n    /// @return The fee\n    function fee() external view returns (uint24);\n\n    /// @notice The pool tick spacing\n    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive\n    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...\n    /// This value is an int24 to avoid casting even though it is always positive.\n    /// @return The tick spacing\n    function tickSpacing() external view returns (int24);\n\n    /// @notice The maximum amount of position liquidity that can use any tick in the range\n    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and\n    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool\n    /// @return The max amount of liquidity per tick\n    function maxLiquidityPerTick() external view returns (uint128);\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Permissioned pool actions\n/// @notice Contains pool methods that may only be called by the factory owner\ninterface IUniswapV3PoolOwnerActions {\n    /// @notice Set the denominator of the protocol's % share of the fees\n    /// @param feeProtocol0 new protocol fee for token0 of the pool\n    /// @param feeProtocol1 new protocol fee for token1 of the pool\n    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;\n\n    /// @notice Collect the protocol fee accrued to the pool\n    /// @param recipient The address to which collected protocol fees should be sent\n    /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1\n    /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0\n    /// @return amount0 The protocol fee collected in token0\n    /// @return amount1 The protocol fee collected in token1\n    function collectProtocol(\n        address recipient,\n        uint128 amount0Requested,\n        uint128 amount1Requested\n    ) external returns (uint128 amount0, uint128 amount1);\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolState.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that can change\n/// @notice These methods compose the pool's state, and can change with any frequency including multiple times\n/// per transaction\ninterface IUniswapV3PoolState {\n    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas\n    /// when accessed externally.\n    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value\n    /// tick The current tick of the pool, i.e. according to the last tick transition that was run.\n    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick\n    /// boundary.\n    /// observationIndex The index of the last oracle observation that was written,\n    /// observationCardinality The current maximum number of observations stored in the pool,\n    /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.\n    /// feeProtocol The protocol fee for both tokens of the pool.\n    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0\n    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.\n    /// unlocked Whether the pool is currently locked to reentrancy\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\n    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool\n    /// @dev This value can overflow the uint256\n    function feeGrowthGlobal0X128() external view returns (uint256);\n\n    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool\n    /// @dev This value can overflow the uint256\n    function feeGrowthGlobal1X128() external view returns (uint256);\n\n    /// @notice The amounts of token0 and token1 that are owed to the protocol\n    /// @dev Protocol fees will never exceed uint128 max in either token\n    function protocolFees() external view returns (uint128 token0, uint128 token1);\n\n    /// @notice The currently in range liquidity available to the pool\n    /// @dev This value has no relationship to the total liquidity across all ticks\n    function liquidity() external view returns (uint128);\n\n    /// @notice Look up information about a specific tick in the pool\n    /// @param tick The tick to look up\n    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or\n    /// tick upper,\n    /// liquidityNet how much liquidity changes when the pool price crosses the tick,\n    /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,\n    /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,\n    /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick\n    /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,\n    /// secondsOutside the seconds spent on the other side of the tick from the current tick,\n    /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.\n    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.\n    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for\n    /// a specific position.\n    function ticks(int24 tick)\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\n    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information\n    function tickBitmap(int16 wordPosition) external view returns (uint256);\n\n    /// @notice Returns the information about a position by the position's key\n    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper\n    /// @return _liquidity The amount of liquidity in the position,\n    /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,\n    /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,\n    /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,\n    /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke\n    function positions(bytes32 key)\n        external\n        view\n        returns (\n            uint128 _liquidity,\n            uint256 feeGrowthInside0LastX128,\n            uint256 feeGrowthInside1LastX128,\n            uint128 tokensOwed0,\n            uint128 tokensOwed1\n        );\n\n    /// @notice Returns data about a specific observation index\n    /// @param index The element of the observations array to fetch\n    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time\n    /// ago, rather than at a specific index in the array.\n    /// @return blockTimestamp The timestamp of the observation,\n    /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,\n    /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,\n    /// Returns initialized whether the observation has been initialized and the values are safe to use\n    function observations(uint256 index)\n        external\n        view\n        returns (\n            uint32 blockTimestamp,\n            int56 tickCumulative,\n            uint160 secondsPerLiquidityCumulativeX128,\n            bool initialized\n        );\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/BitMath.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title BitMath\n/// @dev This library provides functionality for computing bit properties of an unsigned integer\nlibrary BitMath {\n    /// @notice Returns the index of the most significant bit of the number,\n    ///     where the least significant bit is at index 0 and the most significant bit is at index 255\n    /// @dev The function satisfies the property:\n    ///     x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1)\n    /// @param x the value for which to compute the most significant bit, must be greater than 0\n    /// @return r the index of the most significant bit\n    function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {\n        require(x > 0);\n\n        if (x >= 0x100000000000000000000000000000000) {\n            x >>= 128;\n            r += 128;\n        }\n        if (x >= 0x10000000000000000) {\n            x >>= 64;\n            r += 64;\n        }\n        if (x >= 0x100000000) {\n            x >>= 32;\n            r += 32;\n        }\n        if (x >= 0x10000) {\n            x >>= 16;\n            r += 16;\n        }\n        if (x >= 0x100) {\n            x >>= 8;\n            r += 8;\n        }\n        if (x >= 0x10) {\n            x >>= 4;\n            r += 4;\n        }\n        if (x >= 0x4) {\n            x >>= 2;\n            r += 2;\n        }\n        if (x >= 0x2) r += 1;\n    }\n\n    /// @notice Returns the index of the least significant bit of the number,\n    ///     where the least significant bit is at index 0 and the most significant bit is at index 255\n    /// @dev The function satisfies the property:\n    ///     (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0)\n    /// @param x the value for which to compute the least significant bit, must be greater than 0\n    /// @return r the index of the least significant bit\n    function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {\n        require(x > 0);\n\n        r = 255;\n        if (x & type(uint128).max > 0) {\n            r -= 128;\n        } else {\n            x >>= 128;\n        }\n        if (x & type(uint64).max > 0) {\n            r -= 64;\n        } else {\n            x >>= 64;\n        }\n        if (x & type(uint32).max > 0) {\n            r -= 32;\n        } else {\n            x >>= 32;\n        }\n        if (x & type(uint16).max > 0) {\n            r -= 16;\n        } else {\n            x >>= 16;\n        }\n        if (x & type(uint8).max > 0) {\n            r -= 8;\n        } else {\n            x >>= 8;\n        }\n        if (x & 0xf > 0) {\n            r -= 4;\n        } else {\n            x >>= 4;\n        }\n        if (x & 0x3 > 0) {\n            r -= 2;\n        } else {\n            x >>= 2;\n        }\n        if (x & 0x1 > 0) r -= 1;\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/FixedPoint128.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.4.0;\n\n/// @title FixedPoint128\n/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)\nlibrary FixedPoint128 {\n    uint256 internal constant Q128 = 0x100000000000000000000000000000000;\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/FixedPoint96.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.4.0;\n\n/// @title FixedPoint96\n/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)\n/// @dev Used in SqrtPriceMath.sol\nlibrary FixedPoint96 {\n    uint8 internal constant RESOLUTION = 96;\n    uint256 internal constant Q96 = 0x1000000000000000000000000;\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/FullMath.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.0;\n\n/// @title Contains 512-bit math functions\n/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision\n/// @dev Handles \"phantom overflow\" i.e., allows multiplication and division where an intermediate value overflows 256 bits\nlibrary FullMath {\n    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n    /// @param a The multiplicand\n    /// @param b The multiplier\n    /// @param denominator The divisor\n    /// @return result The 256-bit result\n    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv\n    function mulDiv(\n        uint256 a,\n        uint256 b,\n        uint256 denominator\n    ) internal pure returns (uint256 result) {\n        // 512-bit multiply [prod1 prod0] = a * b\n        // Compute the product mod 2**256 and mod 2**256 - 1\n        // then use the Chinese Remainder Theorem to reconstruct\n        // the 512 bit result. The result is stored in two 256\n        // variables such that product = prod1 * 2**256 + prod0\n        uint256 prod0; // Least significant 256 bits of the product\n        uint256 prod1; // Most significant 256 bits of the product\n        assembly {\n            let mm := mulmod(a, b, not(0))\n            prod0 := mul(a, b)\n            prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n        }\n\n        // Handle non-overflow cases, 256 by 256 division\n        if (prod1 == 0) {\n            require(denominator > 0);\n            assembly {\n                result := div(prod0, denominator)\n            }\n            return result;\n        }\n\n        // Make sure the result is less than 2**256.\n        // Also prevents denominator == 0\n        require(denominator > prod1);\n\n        ///////////////////////////////////////////////\n        // 512 by 256 division.\n        ///////////////////////////////////////////////\n\n        // Make division exact by subtracting the remainder from [prod1 prod0]\n        // Compute remainder using mulmod\n        uint256 remainder;\n        assembly {\n            remainder := mulmod(a, b, denominator)\n        }\n        // Subtract 256 bit number from 512 bit number\n        assembly {\n            prod1 := sub(prod1, gt(remainder, prod0))\n            prod0 := sub(prod0, remainder)\n        }\n\n        // Factor powers of two out of denominator\n        // Compute largest power of two divisor of denominator.\n        // Always >= 1.\n        uint256 twos = -denominator & denominator;\n        // Divide denominator by power of two\n        assembly {\n            denominator := div(denominator, twos)\n        }\n\n        // Divide [prod1 prod0] by the factors of two\n        assembly {\n            prod0 := div(prod0, twos)\n        }\n        // Shift in bits from prod1 into prod0. For this we need\n        // to flip `twos` such that it is 2**256 / twos.\n        // If twos is zero, then it becomes one\n        assembly {\n            twos := add(div(sub(0, twos), twos), 1)\n        }\n        prod0 |= prod1 * twos;\n\n        // Invert denominator mod 2**256\n        // Now that denominator is an odd number, it has an inverse\n        // modulo 2**256 such that denominator * inv = 1 mod 2**256.\n        // Compute the inverse by starting with a seed that is correct\n        // correct for four bits. That is, denominator * inv = 1 mod 2**4\n        uint256 inv = (3 * denominator) ^ 2;\n        // Now use Newton-Raphson iteration to improve the precision.\n        // Thanks to Hensel's lifting lemma, this also works in modular\n        // arithmetic, doubling the correct bits in each step.\n        inv *= 2 - denominator * inv; // inverse mod 2**8\n        inv *= 2 - denominator * inv; // inverse mod 2**16\n        inv *= 2 - denominator * inv; // inverse mod 2**32\n        inv *= 2 - denominator * inv; // inverse mod 2**64\n        inv *= 2 - denominator * inv; // inverse mod 2**128\n        inv *= 2 - denominator * inv; // inverse mod 2**256\n\n        // Because the division is now exact we can divide by multiplying\n        // with the modular inverse of denominator. This will give us the\n        // correct result modulo 2**256. Since the precoditions guarantee\n        // that the outcome is less than 2**256, this is the final result.\n        // We don't need to compute the high bits of the result and prod1\n        // is no longer required.\n        result = prod0 * inv;\n        return result;\n    }\n\n    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n    /// @param a The multiplicand\n    /// @param b The multiplier\n    /// @param denominator The divisor\n    /// @return result The 256-bit result\n    function mulDivRoundingUp(\n        uint256 a,\n        uint256 b,\n        uint256 denominator\n    ) internal pure returns (uint256 result) {\n        result = mulDiv(a, b, denominator);\n        if (mulmod(a, b, denominator) > 0) {\n            require(result < type(uint256).max);\n            result++;\n        }\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/LICENSE_GPL",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License."
  },
  {
    "path": "test/contracts/src/contracts/libraries/LICENSE_MIT",
    "content": "Copyright (c) 2021 Remco Bloemen\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "test/contracts/src/contracts/libraries/LiquidityMath.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Math library for liquidity\nlibrary LiquidityMath {\n    /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows\n    /// @param x The liquidity before change\n    /// @param y The delta by which liquidity should be changed\n    /// @return z The liquidity delta\n    function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) {\n        if (y < 0) {\n            require((z = x - uint128(-y)) < x, 'LS');\n        } else {\n            require((z = x + uint128(y)) >= x, 'LA');\n        }\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/LowGasSafeMath.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.0;\n\n/// @title Optimized overflow and underflow safe math operations\n/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost\nlibrary LowGasSafeMath {\n    /// @notice Returns x + y, reverts if sum overflows uint256\n    /// @param x The augend\n    /// @param y The addend\n    /// @return z The sum of x and y\n    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        require((z = x + y) >= x);\n    }\n\n    /// @notice Returns x - y, reverts if underflows\n    /// @param x The minuend\n    /// @param y The subtrahend\n    /// @return z The difference of x and y\n    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        require((z = x - y) <= x);\n    }\n\n    /// @notice Returns x * y, reverts if overflows\n    /// @param x The multiplicand\n    /// @param y The multiplier\n    /// @return z The product of x and y\n    function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        require(x == 0 || (z = x * y) / x == y);\n    }\n\n    /// @notice Returns x + y, reverts if overflows or underflows\n    /// @param x The augend\n    /// @param y The addend\n    /// @return z The sum of x and y\n    function add(int256 x, int256 y) internal pure returns (int256 z) {\n        require((z = x + y) >= x == (y >= 0));\n    }\n\n    /// @notice Returns x - y, reverts if overflows or underflows\n    /// @param x The minuend\n    /// @param y The subtrahend\n    /// @return z The difference of x and y\n    function sub(int256 x, int256 y) internal pure returns (int256 z) {\n        require((z = x - y) <= x == (y >= 0));\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/Oracle.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\n/// @title Oracle\n/// @notice Provides price and liquidity data useful for a wide variety of system designs\n/// @dev Instances of stored oracle data, \"observations\", are collected in the oracle array\n/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the\n/// maximum length of the oracle array. New slots will be added when the array is fully populated.\n/// Observations are overwritten when the full length of the oracle array is populated.\n/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe()\nlibrary Oracle {\n    struct Observation {\n        // the block timestamp of the observation\n        uint32 blockTimestamp;\n        // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized\n        int56 tickCumulative;\n        // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized\n        uint160 secondsPerLiquidityCumulativeX128;\n        // whether or not the observation is initialized\n        bool initialized;\n    }\n\n    /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values\n    /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows\n    /// @param last The specified observation to be transformed\n    /// @param blockTimestamp The timestamp of the new observation\n    /// @param tick The active tick at the time of the new observation\n    /// @param liquidity The total in-range liquidity at the time of the new observation\n    /// @return Observation The newly populated observation\n    function transform(\n        Observation memory last,\n        uint32 blockTimestamp,\n        int24 tick,\n        uint128 liquidity\n    ) private pure returns (Observation memory) {\n        uint32 delta = blockTimestamp - last.blockTimestamp;\n        return\n            Observation({\n                blockTimestamp: blockTimestamp,\n                tickCumulative: last.tickCumulative + int56(tick) * delta,\n                secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 +\n                    ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)),\n                initialized: true\n            });\n    }\n\n    /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array\n    /// @param self The stored oracle array\n    /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32\n    /// @return cardinality The number of populated elements in the oracle array\n    /// @return cardinalityNext The new length of the oracle array, independent of population\n    function initialize(Observation[65535] storage self, uint32 time)\n        internal\n        returns (uint16 cardinality, uint16 cardinalityNext)\n    {\n        self[0] = Observation({\n            blockTimestamp: time,\n            tickCumulative: 0,\n            secondsPerLiquidityCumulativeX128: 0,\n            initialized: true\n        });\n        return (1, 1);\n    }\n\n    /// @notice Writes an oracle observation to the array\n    /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally.\n    /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality\n    /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering.\n    /// @param self The stored oracle array\n    /// @param index The index of the observation that was most recently written to the observations array\n    /// @param blockTimestamp The timestamp of the new observation\n    /// @param tick The active tick at the time of the new observation\n    /// @param liquidity The total in-range liquidity at the time of the new observation\n    /// @param cardinality The number of populated elements in the oracle array\n    /// @param cardinalityNext The new length of the oracle array, independent of population\n    /// @return indexUpdated The new index of the most recently written element in the oracle array\n    /// @return cardinalityUpdated The new cardinality of the oracle array\n    function write(\n        Observation[65535] storage self,\n        uint16 index,\n        uint32 blockTimestamp,\n        int24 tick,\n        uint128 liquidity,\n        uint16 cardinality,\n        uint16 cardinalityNext\n    ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) {\n        Observation memory last = self[index];\n\n        // early return if we've already written an observation this block\n        if (last.blockTimestamp == blockTimestamp) return (index, cardinality);\n\n        // if the conditions are right, we can bump the cardinality\n        if (cardinalityNext > cardinality && index == (cardinality - 1)) {\n            cardinalityUpdated = cardinalityNext;\n        } else {\n            cardinalityUpdated = cardinality;\n        }\n\n        indexUpdated = (index + 1) % cardinalityUpdated;\n        self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity);\n    }\n\n    /// @notice Prepares the oracle array to store up to `next` observations\n    /// @param self The stored oracle array\n    /// @param current The current next cardinality of the oracle array\n    /// @param next The proposed next cardinality which will be populated in the oracle array\n    /// @return next The next cardinality which will be populated in the oracle array\n    function grow(\n        Observation[65535] storage self,\n        uint16 current,\n        uint16 next\n    ) internal returns (uint16) {\n        require(current > 0, 'I');\n        // no-op if the passed next value isn't greater than the current next value\n        if (next <= current) return current;\n        // store in each slot to prevent fresh SSTOREs in swaps\n        // this data will not be used because the initialized boolean is still false\n        for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1;\n        return next;\n    }\n\n    /// @notice comparator for 32-bit timestamps\n    /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time\n    /// @param time A timestamp truncated to 32 bits\n    /// @param a A comparison timestamp from which to determine the relative position of `time`\n    /// @param b From which to determine the relative position of `time`\n    /// @return bool Whether `a` is chronologically <= `b`\n    function lte(\n        uint32 time,\n        uint32 a,\n        uint32 b\n    ) private pure returns (bool) {\n        // if there hasn't been overflow, no need to adjust\n        if (a <= time && b <= time) return a <= b;\n\n        uint256 aAdjusted = a > time ? a : a + 2**32;\n        uint256 bAdjusted = b > time ? b : b + 2**32;\n\n        return aAdjusted <= bAdjusted;\n    }\n\n    /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied.\n    /// The result may be the same observation, or adjacent observations.\n    /// @dev The answer must be contained in the array, used when the target is located within the stored observation\n    /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation\n    /// @param self The stored oracle array\n    /// @param time The current block.timestamp\n    /// @param target The timestamp at which the reserved observation should be for\n    /// @param index The index of the observation that was most recently written to the observations array\n    /// @param cardinality The number of populated elements in the oracle array\n    /// @return beforeOrAt The observation recorded before, or at, the target\n    /// @return atOrAfter The observation recorded at, or after, the target\n    function binarySearch(\n        Observation[65535] storage self,\n        uint32 time,\n        uint32 target,\n        uint16 index,\n        uint16 cardinality\n    ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {\n        uint256 l = (index + 1) % cardinality; // oldest observation\n        uint256 r = l + cardinality - 1; // newest observation\n        uint256 i;\n        while (true) {\n            i = (l + r) / 2;\n\n            beforeOrAt = self[i % cardinality];\n\n            // we've landed on an uninitialized tick, keep searching higher (more recently)\n            if (!beforeOrAt.initialized) {\n                l = i + 1;\n                continue;\n            }\n\n            atOrAfter = self[(i + 1) % cardinality];\n\n            bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target);\n\n            // check if we've found the answer!\n            if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break;\n\n            if (!targetAtOrAfter) r = i - 1;\n            else l = i + 1;\n        }\n    }\n\n    /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied\n    /// @dev Assumes there is at least 1 initialized observation.\n    /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp.\n    /// @param self The stored oracle array\n    /// @param time The current block.timestamp\n    /// @param target The timestamp at which the reserved observation should be for\n    /// @param tick The active tick at the time of the returned or simulated observation\n    /// @param index The index of the observation that was most recently written to the observations array\n    /// @param liquidity The total pool liquidity at the time of the call\n    /// @param cardinality The number of populated elements in the oracle array\n    /// @return beforeOrAt The observation which occurred at, or before, the given timestamp\n    /// @return atOrAfter The observation which occurred at, or after, the given timestamp\n    function getSurroundingObservations(\n        Observation[65535] storage self,\n        uint32 time,\n        uint32 target,\n        int24 tick,\n        uint16 index,\n        uint128 liquidity,\n        uint16 cardinality\n    ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {\n        // optimistically set before to the newest observation\n        beforeOrAt = self[index];\n\n        // if the target is chronologically at or after the newest observation, we can early return\n        if (lte(time, beforeOrAt.blockTimestamp, target)) {\n            if (beforeOrAt.blockTimestamp == target) {\n                // if newest observation equals target, we're in the same block, so we can ignore atOrAfter\n                return (beforeOrAt, atOrAfter);\n            } else {\n                // otherwise, we need to transform\n                return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity));\n            }\n        }\n\n        // now, set before to the oldest observation\n        beforeOrAt = self[(index + 1) % cardinality];\n        if (!beforeOrAt.initialized) beforeOrAt = self[0];\n\n        // ensure that the target is chronologically at or after the oldest observation\n        require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD');\n\n        // if we've reached this point, we have to binary search\n        return binarySearch(self, time, target, index, cardinality);\n    }\n\n    /// @dev Reverts if an observation at or before the desired observation timestamp does not exist.\n    /// 0 may be passed as `secondsAgo' to return the current cumulative values.\n    /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values\n    /// at exactly the timestamp between the two observations.\n    /// @param self The stored oracle array\n    /// @param time The current block timestamp\n    /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation\n    /// @param tick The current tick\n    /// @param index The index of the observation that was most recently written to the observations array\n    /// @param liquidity The current in-range pool liquidity\n    /// @param cardinality The number of populated elements in the oracle array\n    /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo`\n    /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo`\n    function observeSingle(\n        Observation[65535] storage self,\n        uint32 time,\n        uint32 secondsAgo,\n        int24 tick,\n        uint16 index,\n        uint128 liquidity,\n        uint16 cardinality\n    ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) {\n        if (secondsAgo == 0) {\n            Observation memory last = self[index];\n            if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity);\n            return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128);\n        }\n\n        uint32 target = time - secondsAgo;\n\n        (Observation memory beforeOrAt, Observation memory atOrAfter) =\n            getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality);\n\n        if (target == beforeOrAt.blockTimestamp) {\n            // we're at the left boundary\n            return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128);\n        } else if (target == atOrAfter.blockTimestamp) {\n            // we're at the right boundary\n            return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128);\n        } else {\n            // we're in the middle\n            uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;\n            uint32 targetDelta = target - beforeOrAt.blockTimestamp;\n            return (\n                beforeOrAt.tickCumulative +\n                    ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) *\n                    targetDelta,\n                beforeOrAt.secondsPerLiquidityCumulativeX128 +\n                    uint160(\n                        (uint256(\n                            atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128\n                        ) * targetDelta) / observationTimeDelta\n                    )\n            );\n        }\n    }\n\n    /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`\n    /// @dev Reverts if `secondsAgos` > oldest observation\n    /// @param self The stored oracle array\n    /// @param time The current block.timestamp\n    /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation\n    /// @param tick The current tick\n    /// @param index The index of the observation that was most recently written to the observations array\n    /// @param liquidity The current in-range pool liquidity\n    /// @param cardinality The number of populated elements in the oracle array\n    /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo`\n    /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`\n    function observe(\n        Observation[65535] storage self,\n        uint32 time,\n        uint32[] memory secondsAgos,\n        int24 tick,\n        uint16 index,\n        uint128 liquidity,\n        uint16 cardinality\n    ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) {\n        require(cardinality > 0, 'I');\n\n        tickCumulatives = new int56[](secondsAgos.length);\n        secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length);\n        for (uint256 i = 0; i < secondsAgos.length; i++) {\n            (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle(\n                self,\n                time,\n                secondsAgos[i],\n                tick,\n                index,\n                liquidity,\n                cardinality\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/Position.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './FullMath.sol';\nimport './FixedPoint128.sol';\nimport './LiquidityMath.sol';\n\n/// @title Position\n/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary\n/// @dev Positions store additional state for tracking fees owed to the position\nlibrary Position {\n    // info stored for each user's position\n    struct Info {\n        // the amount of liquidity owned by this position\n        uint128 liquidity;\n        // fee growth per unit of liquidity as of the last update to liquidity or fees owed\n        uint256 feeGrowthInside0LastX128;\n        uint256 feeGrowthInside1LastX128;\n        // the fees owed to the position owner in token0/token1\n        uint128 tokensOwed0;\n        uint128 tokensOwed1;\n    }\n\n    /// @notice Returns the Info struct of a position, given an owner and position boundaries\n    /// @param self The mapping containing all user positions\n    /// @param owner The address of the position owner\n    /// @param tickLower The lower tick boundary of the position\n    /// @param tickUpper The upper tick boundary of the position\n    /// @return position The position info struct of the given owners' position\n    function get(\n        mapping(bytes32 => Info) storage self,\n        address owner,\n        int24 tickLower,\n        int24 tickUpper\n    ) internal view returns (Position.Info storage position) {\n        position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))];\n    }\n\n    /// @notice Credits accumulated fees to a user's position\n    /// @param self The individual position to update\n    /// @param liquidityDelta The change in pool liquidity as a result of the position update\n    /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries\n    /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries\n    function update(\n        Info storage self,\n        int128 liquidityDelta,\n        uint256 feeGrowthInside0X128,\n        uint256 feeGrowthInside1X128\n    ) internal {\n        Info memory _self = self;\n\n        uint128 liquidityNext;\n        if (liquidityDelta == 0) {\n            require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions\n            liquidityNext = _self.liquidity;\n        } else {\n            liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta);\n        }\n\n        // calculate accumulated fees\n        uint128 tokensOwed0 =\n            uint128(\n                FullMath.mulDiv(\n                    feeGrowthInside0X128 - _self.feeGrowthInside0LastX128,\n                    _self.liquidity,\n                    FixedPoint128.Q128\n                )\n            );\n        uint128 tokensOwed1 =\n            uint128(\n                FullMath.mulDiv(\n                    feeGrowthInside1X128 - _self.feeGrowthInside1LastX128,\n                    _self.liquidity,\n                    FixedPoint128.Q128\n                )\n            );\n\n        // update the position\n        if (liquidityDelta != 0) self.liquidity = liquidityNext;\n        self.feeGrowthInside0LastX128 = feeGrowthInside0X128;\n        self.feeGrowthInside1LastX128 = feeGrowthInside1X128;\n        if (tokensOwed0 > 0 || tokensOwed1 > 0) {\n            // overflow is acceptable, have to withdraw before you hit type(uint128).max fees\n            self.tokensOwed0 += tokensOwed0;\n            self.tokensOwed1 += tokensOwed1;\n        }\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/SafeCast.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Safe casting methods\n/// @notice Contains methods for safely casting between types\nlibrary SafeCast {\n    /// @notice Cast a uint256 to a uint160, revert on overflow\n    /// @param y The uint256 to be downcasted\n    /// @return z The downcasted integer, now type uint160\n    function toUint160(uint256 y) internal pure returns (uint160 z) {\n        require((z = uint160(y)) == y);\n    }\n\n    /// @notice Cast a int256 to a int128, revert on overflow or underflow\n    /// @param y The int256 to be downcasted\n    /// @return z The downcasted integer, now type int128\n    function toInt128(int256 y) internal pure returns (int128 z) {\n        require((z = int128(y)) == y);\n    }\n\n    /// @notice Cast a uint256 to a int256, revert on overflow\n    /// @param y The uint256 to be casted\n    /// @return z The casted integer, now type int256\n    function toInt256(uint256 y) internal pure returns (int256 z) {\n        require(y < 2**255);\n        z = int256(y);\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/SqrtPriceMath.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './LowGasSafeMath.sol';\nimport './SafeCast.sol';\n\nimport './FullMath.sol';\nimport './UnsafeMath.sol';\nimport './FixedPoint96.sol';\n\n/// @title Functions based on Q64.96 sqrt price and liquidity\n/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas\nlibrary SqrtPriceMath {\n    using LowGasSafeMath for uint256;\n    using SafeCast for uint256;\n\n    /// @notice Gets the next sqrt price given a delta of token0\n    /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least\n    /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the\n    /// price less in order to not send too much output.\n    /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),\n    /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).\n    /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta\n    /// @param liquidity The amount of usable liquidity\n    /// @param amount How much of token0 to add or remove from virtual reserves\n    /// @param add Whether to add or remove the amount of token0\n    /// @return The price after adding or removing amount, depending on add\n    function getNextSqrtPriceFromAmount0RoundingUp(\n        uint160 sqrtPX96,\n        uint128 liquidity,\n        uint256 amount,\n        bool add\n    ) internal pure returns (uint160) {\n        // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price\n        if (amount == 0) return sqrtPX96;\n        uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;\n\n        if (add) {\n            uint256 product;\n            if ((product = amount * sqrtPX96) / amount == sqrtPX96) {\n                uint256 denominator = numerator1 + product;\n                if (denominator >= numerator1)\n                    // always fits in 160 bits\n                    return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));\n            }\n\n            return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));\n        } else {\n            uint256 product;\n            // if the product overflows, we know the denominator underflows\n            // in addition, we must check that the denominator does not underflow\n            require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);\n            uint256 denominator = numerator1 - product;\n            return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();\n        }\n    }\n\n    /// @notice Gets the next sqrt price given a delta of token1\n    /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least\n    /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the\n    /// price less in order to not send too much output.\n    /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity\n    /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta\n    /// @param liquidity The amount of usable liquidity\n    /// @param amount How much of token1 to add, or remove, from virtual reserves\n    /// @param add Whether to add, or remove, the amount of token1\n    /// @return The price after adding or removing `amount`\n    function getNextSqrtPriceFromAmount1RoundingDown(\n        uint160 sqrtPX96,\n        uint128 liquidity,\n        uint256 amount,\n        bool add\n    ) internal pure returns (uint160) {\n        // if we're adding (subtracting), rounding down requires rounding the quotient down (up)\n        // in both cases, avoid a mulDiv for most inputs\n        if (add) {\n            uint256 quotient =\n                (\n                    amount <= type(uint160).max\n                        ? (amount << FixedPoint96.RESOLUTION) / liquidity\n                        : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)\n                );\n\n            return uint256(sqrtPX96).add(quotient).toUint160();\n        } else {\n            uint256 quotient =\n                (\n                    amount <= type(uint160).max\n                        ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)\n                        : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)\n                );\n\n            require(sqrtPX96 > quotient);\n            // always fits 160 bits\n            return uint160(sqrtPX96 - quotient);\n        }\n    }\n\n    /// @notice Gets the next sqrt price given an input amount of token0 or token1\n    /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds\n    /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount\n    /// @param liquidity The amount of usable liquidity\n    /// @param amountIn How much of token0, or token1, is being swapped in\n    /// @param zeroForOne Whether the amount in is token0 or token1\n    /// @return sqrtQX96 The price after adding the input amount to token0 or token1\n    function getNextSqrtPriceFromInput(\n        uint160 sqrtPX96,\n        uint128 liquidity,\n        uint256 amountIn,\n        bool zeroForOne\n    ) internal pure returns (uint160 sqrtQX96) {\n        require(sqrtPX96 > 0);\n        require(liquidity > 0);\n\n        // round to make sure that we don't pass the target price\n        return\n            zeroForOne\n                ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)\n                : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);\n    }\n\n    /// @notice Gets the next sqrt price given an output amount of token0 or token1\n    /// @dev Throws if price or liquidity are 0 or the next price is out of bounds\n    /// @param sqrtPX96 The starting price before accounting for the output amount\n    /// @param liquidity The amount of usable liquidity\n    /// @param amountOut How much of token0, or token1, is being swapped out\n    /// @param zeroForOne Whether the amount out is token0 or token1\n    /// @return sqrtQX96 The price after removing the output amount of token0 or token1\n    function getNextSqrtPriceFromOutput(\n        uint160 sqrtPX96,\n        uint128 liquidity,\n        uint256 amountOut,\n        bool zeroForOne\n    ) internal pure returns (uint160 sqrtQX96) {\n        require(sqrtPX96 > 0);\n        require(liquidity > 0);\n\n        // round to make sure that we pass the target price\n        return\n            zeroForOne\n                ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)\n                : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);\n    }\n\n    /// @notice Gets the amount0 delta between two prices\n    /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),\n    /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))\n    /// @param sqrtRatioAX96 A sqrt price\n    /// @param sqrtRatioBX96 Another sqrt price\n    /// @param liquidity The amount of usable liquidity\n    /// @param roundUp Whether to round the amount up or down\n    /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices\n    function getAmount0Delta(\n        uint160 sqrtRatioAX96,\n        uint160 sqrtRatioBX96,\n        uint128 liquidity,\n        bool roundUp\n    ) internal pure returns (uint256 amount0) {\n        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n\n        uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;\n        uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;\n\n        require(sqrtRatioAX96 > 0);\n\n        return\n            roundUp\n                ? UnsafeMath.divRoundingUp(\n                    FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),\n                    sqrtRatioAX96\n                )\n                : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;\n    }\n\n    /// @notice Gets the amount1 delta between two prices\n    /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))\n    /// @param sqrtRatioAX96 A sqrt price\n    /// @param sqrtRatioBX96 Another sqrt price\n    /// @param liquidity The amount of usable liquidity\n    /// @param roundUp Whether to round the amount up, or down\n    /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices\n    function getAmount1Delta(\n        uint160 sqrtRatioAX96,\n        uint160 sqrtRatioBX96,\n        uint128 liquidity,\n        bool roundUp\n    ) internal pure returns (uint256 amount1) {\n        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n\n        return\n            roundUp\n                ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96)\n                : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);\n    }\n\n    /// @notice Helper that gets signed token0 delta\n    /// @param sqrtRatioAX96 A sqrt price\n    /// @param sqrtRatioBX96 Another sqrt price\n    /// @param liquidity The change in liquidity for which to compute the amount0 delta\n    /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices\n    function getAmount0Delta(\n        uint160 sqrtRatioAX96,\n        uint160 sqrtRatioBX96,\n        int128 liquidity\n    ) internal pure returns (int256 amount0) {\n        return\n            liquidity < 0\n                ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()\n                : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();\n    }\n\n    /// @notice Helper that gets signed token1 delta\n    /// @param sqrtRatioAX96 A sqrt price\n    /// @param sqrtRatioBX96 Another sqrt price\n    /// @param liquidity The change in liquidity for which to compute the amount1 delta\n    /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices\n    function getAmount1Delta(\n        uint160 sqrtRatioAX96,\n        uint160 sqrtRatioBX96,\n        int128 liquidity\n    ) internal pure returns (int256 amount1) {\n        return\n            liquidity < 0\n                ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()\n                : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/SwapMath.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './FullMath.sol';\nimport './SqrtPriceMath.sol';\n\n/// @title Computes the result of a swap within ticks\n/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick.\nlibrary SwapMath {\n    /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap\n    /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive\n    /// @param sqrtRatioCurrentX96 The current sqrt price of the pool\n    /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred\n    /// @param liquidity The usable liquidity\n    /// @param amountRemaining How much input or output amount is remaining to be swapped in/out\n    /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip\n    /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target\n    /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap\n    /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap\n    /// @return feeAmount The amount of input that will be taken as a fee\n    function computeSwapStep(\n        uint160 sqrtRatioCurrentX96,\n        uint160 sqrtRatioTargetX96,\n        uint128 liquidity,\n        int256 amountRemaining,\n        uint24 feePips\n    )\n        internal\n        pure\n        returns (\n            uint160 sqrtRatioNextX96,\n            uint256 amountIn,\n            uint256 amountOut,\n            uint256 feeAmount\n        )\n    {\n        bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96;\n        bool exactIn = amountRemaining >= 0;\n\n        if (exactIn) {\n            uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6);\n            amountIn = zeroForOne\n                ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)\n                : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true);\n            if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96;\n            else\n                sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput(\n                    sqrtRatioCurrentX96,\n                    liquidity,\n                    amountRemainingLessFee,\n                    zeroForOne\n                );\n        } else {\n            amountOut = zeroForOne\n                ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false)\n                : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false);\n            if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96;\n            else\n                sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput(\n                    sqrtRatioCurrentX96,\n                    liquidity,\n                    uint256(-amountRemaining),\n                    zeroForOne\n                );\n        }\n\n        bool max = sqrtRatioTargetX96 == sqrtRatioNextX96;\n\n        // get the input/output amounts\n        if (zeroForOne) {\n            amountIn = max && exactIn\n                ? amountIn\n                : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true);\n            amountOut = max && !exactIn\n                ? amountOut\n                : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false);\n        } else {\n            amountIn = max && exactIn\n                ? amountIn\n                : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true);\n            amountOut = max && !exactIn\n                ? amountOut\n                : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false);\n        }\n\n        // cap the output amount to not exceed the remaining output amount\n        if (!exactIn && amountOut > uint256(-amountRemaining)) {\n            amountOut = uint256(-amountRemaining);\n        }\n\n        if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) {\n            // we didn't reach the target, so take the remainder of the maximum input as fee\n            feeAmount = uint256(amountRemaining) - amountIn;\n        } else {\n            feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips);\n        }\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/Tick.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './LowGasSafeMath.sol';\nimport './SafeCast.sol';\n\nimport './TickMath.sol';\nimport './LiquidityMath.sol';\n\n/// @title Tick\n/// @notice Contains functions for managing tick processes and relevant calculations\nlibrary Tick {\n    using LowGasSafeMath for int256;\n    using SafeCast for int256;\n\n    // info stored for each initialized individual tick\n    struct Info {\n        // the total position liquidity that references this tick\n        uint128 liquidityGross;\n        // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left),\n        int128 liquidityNet;\n        // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)\n        // only has relative meaning, not absolute — the value depends on when the tick is initialized\n        uint256 feeGrowthOutside0X128;\n        uint256 feeGrowthOutside1X128;\n        // the cumulative tick value on the other side of the tick\n        int56 tickCumulativeOutside;\n        // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)\n        // only has relative meaning, not absolute — the value depends on when the tick is initialized\n        uint160 secondsPerLiquidityOutsideX128;\n        // the seconds spent on the other side of the tick (relative to the current tick)\n        // only has relative meaning, not absolute — the value depends on when the tick is initialized\n        uint32 secondsOutside;\n        // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0\n        // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks\n        bool initialized;\n    }\n\n    /// @notice Derives max liquidity per tick from given tick spacing\n    /// @dev Executed within the pool constructor\n    /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing`\n    ///     e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ...\n    /// @return The max liquidity per tick\n    function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) {\n        int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing;\n        int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing;\n        uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1;\n        return type(uint128).max / numTicks;\n    }\n\n    /// @notice Retrieves fee growth data\n    /// @param self The mapping containing all tick information for initialized ticks\n    /// @param tickLower The lower tick boundary of the position\n    /// @param tickUpper The upper tick boundary of the position\n    /// @param tickCurrent The current tick\n    /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0\n    /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1\n    /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries\n    /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries\n    function getFeeGrowthInside(\n        mapping(int24 => Tick.Info) storage self,\n        int24 tickLower,\n        int24 tickUpper,\n        int24 tickCurrent,\n        uint256 feeGrowthGlobal0X128,\n        uint256 feeGrowthGlobal1X128\n    ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {\n        Info storage lower = self[tickLower];\n        Info storage upper = self[tickUpper];\n\n        // calculate fee growth below\n        uint256 feeGrowthBelow0X128;\n        uint256 feeGrowthBelow1X128;\n        if (tickCurrent >= tickLower) {\n            feeGrowthBelow0X128 = lower.feeGrowthOutside0X128;\n            feeGrowthBelow1X128 = lower.feeGrowthOutside1X128;\n        } else {\n            feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128;\n            feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128;\n        }\n\n        // calculate fee growth above\n        uint256 feeGrowthAbove0X128;\n        uint256 feeGrowthAbove1X128;\n        if (tickCurrent < tickUpper) {\n            feeGrowthAbove0X128 = upper.feeGrowthOutside0X128;\n            feeGrowthAbove1X128 = upper.feeGrowthOutside1X128;\n        } else {\n            feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128;\n            feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128;\n        }\n\n        feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128;\n        feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128;\n    }\n\n    /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa\n    /// @param self The mapping containing all tick information for initialized ticks\n    /// @param tick The tick that will be updated\n    /// @param tickCurrent The current tick\n    /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)\n    /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0\n    /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1\n    /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool\n    /// @param tickCumulative The tick * time elapsed since the pool was first initialized\n    /// @param time The current block timestamp cast to a uint32\n    /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick\n    /// @param maxLiquidity The maximum liquidity allocation for a single tick\n    /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa\n    function update(\n        mapping(int24 => Tick.Info) storage self,\n        int24 tick,\n        int24 tickCurrent,\n        int128 liquidityDelta,\n        uint256 feeGrowthGlobal0X128,\n        uint256 feeGrowthGlobal1X128,\n        uint160 secondsPerLiquidityCumulativeX128,\n        int56 tickCumulative,\n        uint32 time,\n        bool upper,\n        uint128 maxLiquidity\n    ) internal returns (bool flipped) {\n        Tick.Info storage info = self[tick];\n\n        uint128 liquidityGrossBefore = info.liquidityGross;\n        uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta);\n\n        require(liquidityGrossAfter <= maxLiquidity, 'LO');\n\n        flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0);\n\n        if (liquidityGrossBefore == 0) {\n            // by convention, we assume that all growth before a tick was initialized happened _below_ the tick\n            if (tick <= tickCurrent) {\n                info.feeGrowthOutside0X128 = feeGrowthGlobal0X128;\n                info.feeGrowthOutside1X128 = feeGrowthGlobal1X128;\n                info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128;\n                info.tickCumulativeOutside = tickCumulative;\n                info.secondsOutside = time;\n            }\n            info.initialized = true;\n        }\n\n        info.liquidityGross = liquidityGrossAfter;\n\n        // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed)\n        info.liquidityNet = upper\n            ? int256(info.liquidityNet).sub(liquidityDelta).toInt128()\n            : int256(info.liquidityNet).add(liquidityDelta).toInt128();\n    }\n\n    /// @notice Clears tick data\n    /// @param self The mapping containing all initialized tick information for initialized ticks\n    /// @param tick The tick that will be cleared\n    function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal {\n        delete self[tick];\n    }\n\n    /// @notice Transitions to next tick as needed by price movement\n    /// @param self The mapping containing all tick information for initialized ticks\n    /// @param tick The destination tick of the transition\n    /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0\n    /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1\n    /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity\n    /// @param tickCumulative The tick * time elapsed since the pool was first initialized\n    /// @param time The current block.timestamp\n    /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)\n    function cross(\n        mapping(int24 => Tick.Info) storage self,\n        int24 tick,\n        uint256 feeGrowthGlobal0X128,\n        uint256 feeGrowthGlobal1X128,\n        uint160 secondsPerLiquidityCumulativeX128,\n        int56 tickCumulative,\n        uint32 time\n    ) internal returns (int128 liquidityNet) {\n        Tick.Info storage info = self[tick];\n        info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128;\n        info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128;\n        info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128;\n        info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside;\n        info.secondsOutside = time - info.secondsOutside;\n        liquidityNet = info.liquidityNet;\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/TickBitmap.sol",
    "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './BitMath.sol';\nimport \"hardhat/console.sol\";\n\n/// @title Packed tick initialized state library\n/// @notice Stores a packed mapping of tick index to its initialized state\n/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.\nlibrary TickBitmap {\n    /// @notice Computes the position in the mapping where the initialized bit for a tick lives\n    /// @param tick The tick for which to compute the position\n    /// @return wordPos The key in the mapping containing the word in which the bit is stored\n    /// @return bitPos The bit position in the word where the flag is stored\n    function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) {\n        wordPos = int16(tick >> 8);\n        bitPos = uint8(tick % 256);\n    }\n\n    /// @notice Flips the initialized state for a given tick from false to true, or vice versa\n    /// @param self The mapping in which to flip the tick\n    /// @param tick The tick to flip\n    /// @param tickSpacing The spacing between usable ticks\n    function flipTick(\n        mapping(int16 => uint256) storage self,\n        int24 tick,\n        int24 tickSpacing\n    ) internal {\n        require(tick % tickSpacing == 0); // ensure that the tick is spaced\n        (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing);\n        uint256 mask = 1 << bitPos;\n        self[wordPos] ^= mask;\n    }\n\n    /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either\n    /// to the left (less than or equal to) or right (greater than) of the given tick\n    /// @param self The mapping in which to compute the next initialized tick\n    /// @param tick The starting tick\n    /// @param tickSpacing The spacing between usable ticks\n    /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick)\n    /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick\n    /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks\n    function nextInitializedTickWithinOneWord(\n        mapping(int16 => uint256) storage self,\n        int24 tick,\n        int24 tickSpacing,\n        bool lte\n    ) internal view returns (int24 next, bool initialized) {\n        int24 compressed = tick / tickSpacing;\n        if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity\n        if (lte) {\n            (int16 wordPos, uint8 bitPos) = position(compressed);\n            // all the 1s at or to the right of the current bitPos\n            uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);\n            uint256 masked = self[wordPos] & mask;\n\n            // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word\n            initialized = masked != 0;\n            // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick\n            next = initialized\n                ? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing\n                : (compressed - int24(bitPos)) * tickSpacing;\n        } else {\n            // start from the word of the next tick, since the current tick state doesn't matter\n            (int16 wordPos, uint8 bitPos) = position(compressed + 1);\n            // all the 1s at or to the left of the bitPos\n            uint256 mask = ~((1 << bitPos) - 1);\n            uint256 masked = self[wordPos] & mask;\n\n            // if there are no initialized ticks to the left of the current tick, return leftmost in the word\n            initialized = masked != 0;\n            // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick\n            next = initialized\n                ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing\n                : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing;\n        }\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/TickMath.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Math library for computing sqrt prices from ticks and vice versa\n/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports\n/// prices between 2**-128 and 2**128\nlibrary TickMath {\n    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128\n    int24 internal constant MIN_TICK = -887272;\n    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128\n    int24 internal constant MAX_TICK = -MIN_TICK;\n\n    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)\n    uint160 internal constant MIN_SQRT_RATIO = 4295128739;\n    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)\n    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;\n\n    /// @notice Calculates sqrt(1.0001^tick) * 2^96\n    /// @dev Throws if |tick| > max tick\n    /// @param tick The input tick for the above formula\n    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)\n    /// at the given tick\n    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {\n        uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n        require(absTick <= uint256(MAX_TICK), 'T');\n\n        uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;\n        if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;\n        if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;\n        if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;\n        if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;\n        if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;\n        if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;\n        if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;\n        if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;\n        if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;\n        if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;\n        if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;\n        if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;\n        if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;\n        if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;\n        if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;\n        if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;\n        if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;\n        if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;\n        if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;\n\n        if (tick > 0) ratio = type(uint256).max / ratio;\n\n        // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.\n        // we then downcast because we know the result always fits within 160 bits due to our tick input constraint\n        // we round up in the division so getTickAtSqrtRatio of the output price is always consistent\n        sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n    }\n\n    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio\n    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may\n    /// ever return.\n    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96\n    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio\n    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {\n        // second inequality must be < because the price can never reach the price at the max tick\n        require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R');\n        uint256 ratio = uint256(sqrtPriceX96) << 32;\n\n        uint256 r = ratio;\n        uint256 msb = 0;\n\n        assembly {\n            let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))\n            msb := or(msb, f)\n            r := shr(f, r)\n        }\n        assembly {\n            let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))\n            msb := or(msb, f)\n            r := shr(f, r)\n        }\n        assembly {\n            let f := shl(5, gt(r, 0xFFFFFFFF))\n            msb := or(msb, f)\n            r := shr(f, r)\n        }\n        assembly {\n            let f := shl(4, gt(r, 0xFFFF))\n            msb := or(msb, f)\n            r := shr(f, r)\n        }\n        assembly {\n            let f := shl(3, gt(r, 0xFF))\n            msb := or(msb, f)\n            r := shr(f, r)\n        }\n        assembly {\n            let f := shl(2, gt(r, 0xF))\n            msb := or(msb, f)\n            r := shr(f, r)\n        }\n        assembly {\n            let f := shl(1, gt(r, 0x3))\n            msb := or(msb, f)\n            r := shr(f, r)\n        }\n        assembly {\n            let f := gt(r, 0x1)\n            msb := or(msb, f)\n        }\n\n        if (msb >= 128) r = ratio >> (msb - 127);\n        else r = ratio << (127 - msb);\n\n        int256 log_2 = (int256(msb) - 128) << 64;\n\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(63, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(62, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(61, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(60, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(59, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(58, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(57, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(56, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(55, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(54, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(53, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(52, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(51, f))\n            r := shr(f, r)\n        }\n        assembly {\n            r := shr(127, mul(r, r))\n            let f := shr(128, r)\n            log_2 := or(log_2, shl(50, f))\n        }\n\n        int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number\n\n        int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);\n        int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);\n\n        tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/TransferHelper.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.6.0;\n\nimport '../interfaces/IERC20Minimal.sol';\n\n/// @title TransferHelper\n/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false\nlibrary TransferHelper {\n    /// @notice Transfers tokens from msg.sender to a recipient\n    /// @dev Calls transfer on token contract, errors with TF if transfer fails\n    /// @param token The contract address of the token which will be transferred\n    /// @param to The recipient of the transfer\n    /// @param value The value of the transfer\n    function safeTransfer(\n        address token,\n        address to,\n        uint256 value\n    ) internal {\n        (bool success, bytes memory data) =\n            token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value));\n        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF');\n    }\n}\n"
  },
  {
    "path": "test/contracts/src/contracts/libraries/UnsafeMath.sol",
    "content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Math functions that do not check inputs or outputs\n/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks\nlibrary UnsafeMath {\n    /// @notice Returns ceil(x / y)\n    /// @dev division by 0 has unspecified behavior, and must be checked externally\n    /// @param x The dividend\n    /// @param y The divisor\n    /// @return z The quotient, ceil(x / y)\n    function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {\n        assembly {\n            z := add(div(x, y), gt(mod(x, y), 0))\n        }\n    }\n}\n"
  },
  {
    "path": "test/shared/MockableTick.ts",
    "content": "import JSBI from \"jsbi\";\nimport { Tick } from \"../../src/model/Tick\";\n\nexport class MockableTick extends Tick {\n  // [CAUTION] this is for testing only\n  // specifically for easily setting up test cases\n  // DO NOT use this function since it will break the data integrity of the tick\n  updateProperties(\n    liquidityGross: JSBI,\n    liquidityNet: JSBI,\n    feeGrowthOutside0X128: JSBI,\n    feeGrowthOutside1X128: JSBI\n  ): Tick {\n    this._liquidityGross = liquidityGross;\n    this._liquidityNet = liquidityNet;\n    this._feeGrowthOutside0X128 = feeGrowthOutside0X128;\n    this._feeGrowthOutside1X128 = feeGrowthOutside1X128;\n    return this;\n  }\n}\n"
  },
  {
    "path": "test/shared/checkObservationEquals.ts",
    "content": "import { BigNumber, BigNumberish } from \"ethers\";\nimport { expect } from \"./expect\";\n\n// helper function because we cannot do a simple deep equals with the\n// observation result object returned from ethers because it extends array\nexport default function checkObservationEquals(\n  {\n    tickCumulative,\n    blockTimestamp,\n    initialized,\n    secondsPerLiquidityCumulativeX128,\n  }: {\n    tickCumulative: BigNumber;\n    secondsPerLiquidityCumulativeX128: BigNumber;\n    initialized: boolean;\n    blockTimestamp: number;\n  },\n  expected: {\n    tickCumulative: BigNumberish;\n    secondsPerLiquidityCumulativeX128: BigNumberish;\n    initialized: boolean;\n    blockTimestamp: number;\n  }\n) {\n  expect(\n    {\n      initialized,\n      blockTimestamp,\n      tickCumulative: tickCumulative.toString(),\n      secondsPerLiquidityCumulativeX128:\n        secondsPerLiquidityCumulativeX128.toString(),\n    },\n    `observation is equivalent`\n  ).to.deep.eq({\n    ...expected,\n    tickCumulative: expected.tickCumulative.toString(),\n    secondsPerLiquidityCumulativeX128:\n      expected.secondsPerLiquidityCumulativeX128.toString(),\n  });\n}\n"
  },
  {
    "path": "test/shared/expect.ts",
    "content": "import { expect, use } from \"chai\";\nimport { solidity } from \"ethereum-waffle\";\nimport { jestSnapshotPlugin } from \"mocha-chai-jest-snapshot\";\n\nuse(solidity);\nuse(jestSnapshotPlugin());\n\nexport { expect };\n"
  },
  {
    "path": "test/shared/fixtures.ts",
    "content": "import { BigNumber } from \"ethers\";\nimport { ethers } from \"hardhat\";\nimport { MockTimeUniswapV3Pool } from \"../../typechain/MockTimeUniswapV3Pool\";\nimport { TestERC20 } from \"../../typechain/TestERC20\";\nimport { UniswapV3Factory } from \"../../typechain/UniswapV3Factory\";\nimport { TestUniswapV3Callee } from \"../../typechain/TestUniswapV3Callee\";\nimport { TestUniswapV3Router } from \"../../typechain/TestUniswapV3Router\";\nimport { MockTimeUniswapV3PoolDeployer } from \"../../typechain/MockTimeUniswapV3PoolDeployer\";\n\nimport { Fixture } from \"ethereum-waffle\";\n\ninterface FactoryFixture {\n  factory: UniswapV3Factory;\n}\n\nasync function factoryFixture(): Promise<FactoryFixture> {\n  const factoryFactory = await ethers.getContractFactory(\"UniswapV3Factory\");\n  const factory = (await factoryFactory.deploy()) as UniswapV3Factory;\n  return { factory };\n}\n\ninterface TokensFixture {\n  token0: TestERC20;\n  token1: TestERC20;\n  token2: TestERC20;\n}\n\nasync function tokensFixture(): Promise<TokensFixture> {\n  const tokenFactory = await ethers.getContractFactory(\"TestERC20\");\n  const tokenA = (await tokenFactory.deploy(\n    BigNumber.from(2).pow(255)\n  )) as TestERC20;\n  const tokenB = (await tokenFactory.deploy(\n    BigNumber.from(2).pow(255)\n  )) as TestERC20;\n  const tokenC = (await tokenFactory.deploy(\n    BigNumber.from(2).pow(255)\n  )) as TestERC20;\n\n  const [token0, token1, token2] = [tokenA, tokenB, tokenC].sort(\n    (tokenA, tokenB) =>\n      tokenA.address.toLowerCase() < tokenB.address.toLowerCase() ? -1 : 1\n  );\n\n  return { token0, token1, token2 };\n}\n\ntype TokensAndFactoryFixture = FactoryFixture & TokensFixture;\n\ninterface PoolFixture extends TokensAndFactoryFixture {\n  swapTargetCallee: TestUniswapV3Callee;\n  swapTargetRouter: TestUniswapV3Router;\n  createPool(\n    fee: number,\n    tickSpacing: number,\n    firstToken?: TestERC20,\n    secondToken?: TestERC20\n  ): Promise<MockTimeUniswapV3Pool>;\n}\n\n// Monday, October 5, 2020 9:00:00 AM GMT-05:00\nexport const TEST_POOL_START_TIME = 1601906400;\n\nexport const poolFixture: Fixture<PoolFixture> =\n  async function (): Promise<PoolFixture> {\n    const { factory } = await factoryFixture();\n    const { token0, token1, token2 } = await tokensFixture();\n\n    const MockTimeUniswapV3PoolDeployerFactory =\n      await ethers.getContractFactory(\"MockTimeUniswapV3PoolDeployer\");\n    const MockTimeUniswapV3PoolFactory = await ethers.getContractFactory(\n      \"MockTimeUniswapV3Pool\"\n    );\n\n    const calleeContractFactory = await ethers.getContractFactory(\n      \"TestUniswapV3Callee\"\n    );\n    const routerContractFactory = await ethers.getContractFactory(\n      \"TestUniswapV3Router\"\n    );\n\n    const swapTargetCallee =\n      (await calleeContractFactory.deploy()) as TestUniswapV3Callee;\n    const swapTargetRouter =\n      (await routerContractFactory.deploy()) as TestUniswapV3Router;\n\n    return {\n      token0,\n      token1,\n      token2,\n      factory,\n      swapTargetCallee,\n      swapTargetRouter,\n      createPool: async (\n        fee,\n        tickSpacing,\n        firstToken = token0,\n        secondToken = token1\n      ) => {\n        const mockTimePoolDeployer =\n          (await MockTimeUniswapV3PoolDeployerFactory.deploy()) as MockTimeUniswapV3PoolDeployer;\n        const tx = await mockTimePoolDeployer.deploy(\n          factory.address,\n          firstToken.address,\n          secondToken.address,\n          fee,\n          tickSpacing\n        );\n\n        const receipt = await tx.wait();\n        const poolAddress = receipt.events?.[0].args?.pool as string;\n        return MockTimeUniswapV3PoolFactory.attach(\n          poolAddress\n        ) as MockTimeUniswapV3Pool;\n      },\n    };\n  };\n"
  },
  {
    "path": "test/shared/format.ts",
    "content": "import { Decimal } from \"decimal.js\";\nimport { BigNumberish } from \"ethers\";\n\nexport function formatTokenAmount(num: BigNumberish): string {\n  return new Decimal(num.toString())\n    .dividedBy(new Decimal(10).pow(18))\n    .toPrecision(5);\n}\n\nexport function formatPrice(price: BigNumberish): string {\n  return new Decimal(price.toString())\n    .dividedBy(new Decimal(2).pow(96))\n    .pow(2)\n    .toPrecision(5);\n}\n"
  },
  {
    "path": "test/shared/snapshotGasCost.ts",
    "content": "import {\n  TransactionReceipt,\n  TransactionResponse,\n} from \"@ethersproject/abstract-provider\";\nimport { expect } from \"./expect\";\nimport { Contract, BigNumber, ContractTransaction } from \"ethers\";\n\nexport default async function snapshotGasCost(\n  x:\n    | TransactionResponse\n    | Promise<TransactionResponse>\n    | ContractTransaction\n    | Promise<ContractTransaction>\n    | TransactionReceipt\n    | Promise<BigNumber>\n    | BigNumber\n    | Contract\n    | Promise<Contract>\n): Promise<void> {\n  const resolved = await x;\n  if (\"deployTransaction\" in resolved) {\n    const receipt = await resolved.deployTransaction.wait();\n    expect(receipt.gasUsed.toNumber()).toMatchSnapshot();\n  } else if (\"wait\" in resolved) {\n    const waited = await resolved.wait();\n    expect(waited.gasUsed.toNumber()).toMatchSnapshot();\n  } else if (BigNumber.isBigNumber(resolved)) {\n    expect(resolved.toNumber()).toMatchSnapshot();\n  }\n}\n"
  },
  {
    "path": "test/shared/utilities.ts",
    "content": "import bn from \"bignumber.js\";\n// import {\n//   BigNumber,\n//   BigNumberish,\n//   constants,\n//   Contract,\n//   ContractTransaction,\n//   utils,\n//   Wallet,\n// } from \"ethers\";\n// import { TestUniswapV3Callee } from '../../typechain/TestUniswapV3Callee'\n// import { TestUniswapV3Router } from '../../typechain/TestUniswapV3Router'\n// import { MockTimeUniswapV3Pool } from '../../typechain/MockTimeUniswapV3Pool'\n// import { TestERC20 } from '../../typechain/TestERC20'\n\nimport JSBI from \"jsbi\";\nimport { MaxUint128 } from \"../../src/enum/InternalConstants\";\n\n// export const MaxUint128 = BigNumber.from(2).pow(128).sub(1)\n\nexport const getMinTick = (tickSpacing: number) =>\n  Math.ceil(-887272 / tickSpacing) * tickSpacing;\nexport const getMaxTick = (tickSpacing: number) =>\n  Math.floor(887272 / tickSpacing) * tickSpacing;\n// export const getMaxLiquidityPerTick = (tickSpacing: number) =>\n//   BigNumber.from(2)\n//     .pow(128)\n//     .sub(1)\n//     .div((getMaxTick(tickSpacing) - getMinTick(tickSpacing)) / tickSpacing + 1)\n\nexport const getMaxLiquidityPerTick = (tickSpacing: number) =>\n  JSBI.divide(\n    MaxUint128,\n    JSBI.BigInt(\n      (getMaxTick(tickSpacing) - getMinTick(tickSpacing)) / tickSpacing + 1\n    )\n  );\n\n// export const MIN_SQRT_RATIO = BigNumber.from('4295128739')\n// export const MAX_SQRT_RATIO = BigNumber.from('1461446703485210103287273052203988822378723970342')\n\nexport enum FeeAmount {\n  LOW = 500,\n  MEDIUM = 3000,\n  HIGH = 10000,\n}\n\nexport const TICK_SPACINGS: { [amount in FeeAmount]: number } = {\n  [FeeAmount.LOW]: 10,\n  [FeeAmount.MEDIUM]: 60,\n  [FeeAmount.HIGH]: 200,\n};\n\nexport function expandTo18Decimals(n: number): JSBI {\n  return JSBI.multiply(\n    JSBI.BigInt(n),\n    JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))\n  );\n}\n\n// export function getCreate2Address(\n//   factoryAddress: string,\n//   [tokenA, tokenB]: [string, string],\n//   fee: number,\n//   bytecode: string\n// ): string {\n//   const [token0, token1] = tokenA.toLowerCase() < tokenB.toLowerCase() ? [tokenA, tokenB] : [tokenB, tokenA]\n//   const constructorArgumentsEncoded = utils.defaultAbiCoder.encode(\n//     ['address', 'address', 'uint24'],\n//     [token0, token1, fee]\n//   )\n//   const create2Inputs = [\n//     '0xff',\n//     factoryAddress,\n//     // salt\n//     utils.keccak256(constructorArgumentsEncoded),\n//     // init code. bytecode + constructor arguments\n//     utils.keccak256(bytecode),\n//   ]\n//   const sanitizedInputs = `0x${create2Inputs.map((i) => i.slice(2)).join('')}`\n//   return utils.getAddress(`0x${utils.keccak256(sanitizedInputs).slice(-40)}`)\n// }\n\n// bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 })\n\n// returns the sqrt price as a 64x96\nexport function encodePriceSqrt(reserve1: JSBI, reserve0: JSBI): JSBI {\n  bn.config({ EXPONENTIAL_AT: 256 });\n  return JSBI.BigInt(\n    new bn(reserve1.toString())\n      .div(reserve0.toString())\n      .sqrt()\n      .multipliedBy(new bn(2).pow(96))\n      .integerValue(3)\n      .toString()\n  );\n}\n\n// export function getPositionKey(address: string, lowerTick: number, upperTick: number): string {\n//   return utils.keccak256(utils.solidityPack(['address', 'int24', 'int24'], [address, lowerTick, upperTick]))\n// }\n\n// export type SwapFunction = (\n//   amount: BigNumberish,\n//   to: Wallet | string,\n//   sqrtPriceLimitX96?: BigNumberish\n// ) => Promise<ContractTransaction>\n// export type SwapToPriceFunction = (sqrtPriceX96: BigNumberish, to: Wallet | string) => Promise<ContractTransaction>\n// export type FlashFunction = (\n//   amount0: BigNumberish,\n//   amount1: BigNumberish,\n//   to: Wallet | string,\n//   pay0?: BigNumberish,\n//   pay1?: BigNumberish\n// ) => Promise<ContractTransaction>\n// export type MintFunction = (\n//   recipient: string,\n//   tickLower: BigNumberish,\n//   tickUpper: BigNumberish,\n//   liquidity: BigNumberish\n// ) => Promise<ContractTransaction>\n// export interface PoolFunctions {\n//   swapToLowerPrice: SwapToPriceFunction\n//   swapToHigherPrice: SwapToPriceFunction\n//   swapExact0For1: SwapFunction\n//   swap0ForExact1: SwapFunction\n//   swapExact1For0: SwapFunction\n//   swap1ForExact0: SwapFunction\n//   flash: FlashFunction\n//   mint: MintFunction\n// }\n// export function createPoolFunctions({\n//   swapTarget,\n//   token0,\n//   token1,\n//   pool,\n// }: {\n//   swapTarget: TestUniswapV3Callee\n//   token0: TestERC20\n//   token1: TestERC20\n//   pool: MockTimeUniswapV3Pool\n// }): PoolFunctions {\n//   async function swapToSqrtPrice(\n//     inputToken: Contract,\n//     targetPrice: BigNumberish,\n//     to: Wallet | string\n//   ): Promise<ContractTransaction> {\n//     const method = inputToken === token0 ? swapTarget.swapToLowerSqrtPrice : swapTarget.swapToHigherSqrtPrice\n\n//     await inputToken.approve(swapTarget.address, constants.MaxUint256)\n\n//     const toAddress = typeof to === 'string' ? to : to.address\n\n//     return method(pool.address, targetPrice, toAddress)\n//   }\n\n//   async function swap(\n//     inputToken: Contract,\n//     [amountIn, amountOut]: [BigNumberish, BigNumberish],\n//     to: Wallet | string,\n//     sqrtPriceLimitX96?: BigNumberish\n//   ): Promise<ContractTransaction> {\n//     const exactInput = amountOut === 0\n\n//     const method =\n//       inputToken === token0\n//         ? exactInput\n//           ? swapTarget.swapExact0For1\n//           : swapTarget.swap0ForExact1\n//         : exactInput\n//         ? swapTarget.swapExact1For0\n//         : swapTarget.swap1ForExact0\n\n//     if (typeof sqrtPriceLimitX96 === 'undefined') {\n//       if (inputToken === token0) {\n//         sqrtPriceLimitX96 = MIN_SQRT_RATIO.add(1)\n//       } else {\n//         sqrtPriceLimitX96 = MAX_SQRT_RATIO.sub(1)\n//       }\n//     }\n//     await inputToken.approve(swapTarget.address, constants.MaxUint256)\n\n//     const toAddress = typeof to === 'string' ? to : to.address\n\n//     return method(pool.address, exactInput ? amountIn : amountOut, toAddress, sqrtPriceLimitX96)\n//   }\n\n//   const swapToLowerPrice: SwapToPriceFunction = (sqrtPriceX96, to) => {\n//     return swapToSqrtPrice(token0, sqrtPriceX96, to)\n//   }\n\n//   const swapToHigherPrice: SwapToPriceFunction = (sqrtPriceX96, to) => {\n//     return swapToSqrtPrice(token1, sqrtPriceX96, to)\n//   }\n\n//   const swapExact0For1: SwapFunction = (amount, to, sqrtPriceLimitX96) => {\n//     return swap(token0, [amount, 0], to, sqrtPriceLimitX96)\n//   }\n\n//   const swap0ForExact1: SwapFunction = (amount, to, sqrtPriceLimitX96) => {\n//     return swap(token0, [0, amount], to, sqrtPriceLimitX96)\n//   }\n\n//   const swapExact1For0: SwapFunction = (amount, to, sqrtPriceLimitX96) => {\n//     return swap(token1, [amount, 0], to, sqrtPriceLimitX96)\n//   }\n\n//   const swap1ForExact0: SwapFunction = (amount, to, sqrtPriceLimitX96) => {\n//     return swap(token1, [0, amount], to, sqrtPriceLimitX96)\n//   }\n\n//   const mint: MintFunction = async (recipient, tickLower, tickUpper, liquidity) => {\n//     await token0.approve(swapTarget.address, constants.MaxUint256)\n//     await token1.approve(swapTarget.address, constants.MaxUint256)\n//     return swapTarget.mint(pool.address, recipient, tickLower, tickUpper, liquidity)\n//   }\n\n//   const flash: FlashFunction = async (amount0, amount1, to, pay0?: BigNumberish, pay1?: BigNumberish) => {\n//     const fee = await pool.fee()\n//     if (typeof pay0 === 'undefined') {\n//       pay0 = BigNumber.from(amount0)\n//         .mul(fee)\n//         .add(1e6 - 1)\n//         .div(1e6)\n//         .add(amount0)\n//     }\n//     if (typeof pay1 === 'undefined') {\n//       pay1 = BigNumber.from(amount1)\n//         .mul(fee)\n//         .add(1e6 - 1)\n//         .div(1e6)\n//         .add(amount1)\n//     }\n//     return swapTarget.flash(pool.address, typeof to === 'string' ? to : to.address, amount0, amount1, pay0, pay1)\n//   }\n\n//   return {\n//     swapToLowerPrice,\n//     swapToHigherPrice,\n//     swapExact0For1,\n//     swap0ForExact1,\n//     swapExact1For0,\n//     swap1ForExact0,\n//     mint,\n//     flash,\n//   }\n// }\n\n// export interface MultiPoolFunctions {\n//   swapForExact0Multi: SwapFunction\n//   swapForExact1Multi: SwapFunction\n// }\n\n// export function createMultiPoolFunctions({\n//   inputToken,\n//   swapTarget,\n//   poolInput,\n//   poolOutput,\n// }: {\n//   inputToken: TestERC20\n//   swapTarget: TestUniswapV3Router\n//   poolInput: MockTimeUniswapV3Pool\n//   poolOutput: MockTimeUniswapV3Pool\n// }): MultiPoolFunctions {\n//   async function swapForExact0Multi(amountOut: BigNumberish, to: Wallet | string): Promise<ContractTransaction> {\n//     const method = swapTarget.swapForExact0Multi\n//     await inputToken.approve(swapTarget.address, constants.MaxUint256)\n//     const toAddress = typeof to === 'string' ? to : to.address\n//     return method(toAddress, poolInput.address, poolOutput.address, amountOut)\n//   }\n\n//   async function swapForExact1Multi(amountOut: BigNumberish, to: Wallet | string): Promise<ContractTransaction> {\n//     const method = swapTarget.swapForExact1Multi\n//     await inputToken.approve(swapTarget.address, constants.MaxUint256)\n//     const toAddress = typeof to === 'string' ? to : to.address\n//     return method(toAddress, poolInput.address, poolOutput.address, amountOut)\n//   }\n\n//   return {\n//     swapForExact0Multi,\n//     swapForExact1Multi,\n//   }\n// }\n"
  },
  {
    "path": "test/stubs/MockTimeUniswapV3Pool.ts",
    "content": "import { EventFilter, BigNumber, BigNumberish, Signer } from \"ethers\";\nimport { ContractTransaction } from \"@ethersproject/contracts\";\nimport {\n  Listener,\n  Provider,\n  TransactionResponse,\n} from \"@ethersproject/providers\";\nimport { BytesLike } from \"@ethersproject/bytes\";\n\nexport class MockTimeUniswapV3Pool {\n  address: string = \"\";\n\n  connect(signerOrProvider: Signer | Provider | string): this {\n    return this;\n  }\n  attach(addressOrName: string): this {\n    return this;\n  }\n  deployed(): Promise<this> {\n    return new Promise((resolve) => {});\n  }\n\n  on(event: EventFilter | string, listener: Listener): this {\n    return this;\n  }\n  once(event: EventFilter | string, listener: Listener): this {\n    return this;\n  }\n  addListener(eventName: EventFilter | string, listener: Listener): this {\n    return this;\n  }\n  removeAllListeners(eventName: EventFilter | string): this {\n    return this;\n  }\n  removeListener(eventName: any, listener: Listener): this {\n    return this;\n  }\n\n  advanceTime(by: BigNumberish): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  burn(\n    tickLower: BigNumberish,\n    tickUpper: BigNumberish,\n    amount: BigNumberish\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  collectStatic(\n    recipient: string,\n    tickLower: BigNumberish,\n    tickUpper: BigNumberish,\n    amount0Requested: BigNumberish,\n    amount1Requested: BigNumberish\n  ): Promise<{\n    amount0: BigNumber;\n    amount1: BigNumber;\n    0: BigNumber;\n    1: BigNumber;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  collect(\n    recipient: string,\n    tickLower: BigNumberish,\n    tickUpper: BigNumberish,\n    amount0Requested: BigNumberish,\n    amount1Requested: BigNumberish\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  collectProtocolStatic(\n    recipient: string,\n    amount0Requested: BigNumberish,\n    amount1Requested: BigNumberish\n  ): Promise<{\n    amount0: BigNumber;\n    amount1: BigNumber;\n    0: BigNumber;\n    1: BigNumber;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  collectProtocol(\n    recipient: string,\n    amount0Requested: BigNumberish,\n    amount1Requested: BigNumberish\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  factory(): Promise<string> {\n    return new Promise((resolve) => {});\n  }\n\n  fee(): Promise<number> {\n    return new Promise((resolve) => {});\n  }\n\n  feeGrowthGlobal0X128(): Promise<BigNumber> {\n    return new Promise((resolve) => {});\n  }\n\n  feeGrowthGlobal1X128(): Promise<BigNumber> {\n    return new Promise((resolve) => {});\n  }\n\n  flash(\n    recipient: string,\n    amount0: BigNumberish,\n    amount1: BigNumberish,\n    data: BytesLike\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  increaseObservationCardinalityNext(\n    observationCardinalityNext: BigNumberish\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  // initialize(sqrtPriceX96: BigNumberish): Promise<ContractTransaction> {\n  //   return new Promise((resolve) => {});\n  // }\n  initialize(sqrtPriceX96: BigNumberish): void {\n    return;\n  }\n\n  liquidity(): Promise<BigNumber> {\n    return new Promise((resolve) => {});\n  }\n\n  maxLiquidityPerTick(): Promise<BigNumber> {\n    return new Promise((resolve) => {});\n  }\n\n  mint(\n    recipient: string,\n    tickLower: BigNumberish,\n    tickUpper: BigNumberish,\n    amount: BigNumberish,\n    data: BytesLike\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  observations(arg0: BigNumberish): Promise<{\n    blockTimestamp: number;\n    tickCumulative: BigNumber;\n    secondsPerLiquidityCumulativeX128: BigNumber;\n    initialized: boolean;\n    0: number;\n    1: BigNumber;\n    2: BigNumber;\n    3: boolean;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  observe(secondsAgos: BigNumberish[]): Promise<{\n    tickCumulatives: BigNumber[];\n    secondsPerLiquidityCumulativeX128s: BigNumber[];\n    0: BigNumber[];\n    1: BigNumber[];\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  positions(arg0: BytesLike): Promise<{\n    liquidity: BigNumber;\n    feeGrowthInside0LastX128: BigNumber;\n    feeGrowthInside1LastX128: BigNumber;\n    tokensOwed0: BigNumber;\n    tokensOwed1: BigNumber;\n    0: BigNumber;\n    1: BigNumber;\n    2: BigNumber;\n    3: BigNumber;\n    4: BigNumber;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  protocolFees(): Promise<{\n    token0: BigNumber;\n    token1: BigNumber;\n    0: BigNumber;\n    1: BigNumber;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  setFeeGrowthGlobal0X128(\n    _feeGrowthGlobal0X128: BigNumberish\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  setFeeGrowthGlobal1X128(\n    _feeGrowthGlobal1X128: BigNumberish\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  setFeeProtocol(\n    feeProtocol0: BigNumberish,\n    feeProtocol1: BigNumberish\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  slot0(): Promise<{\n    sqrtPriceX96: BigNumber;\n    tick: number;\n    observationIndex: number;\n    observationCardinality: number;\n    observationCardinalityNext: number;\n    feeProtocol: number;\n    unlocked: boolean;\n    0: BigNumber;\n    1: number;\n    2: number;\n    3: number;\n    4: number;\n    5: number;\n    6: boolean;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  snapshotCumulativesInside(\n    tickLower: BigNumberish,\n    tickUpper: BigNumberish\n  ): Promise<{\n    tickCumulativeInside: BigNumber;\n    secondsPerLiquidityInsideX128: BigNumber;\n    secondsInside: number;\n    0: BigNumber;\n    1: BigNumber;\n    2: number;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  swap(\n    recipient: string,\n    zeroForOne: boolean,\n    amountSpecified: BigNumberish,\n    sqrtPriceLimitX96: BigNumberish,\n    data: BytesLike\n  ): Promise<ContractTransaction> {\n    return new Promise((resolve) => {});\n  }\n\n  tickBitmap(arg0: BigNumberish): Promise<BigNumber> {\n    return new Promise((resolve) => {});\n  }\n\n  tickSpacing(): Promise<number> {\n    return new Promise((resolve) => {});\n  }\n\n  ticks(arg0: BigNumberish): Promise<{\n    liquidityGross: BigNumber;\n    liquidityNet: BigNumber;\n    feeGrowthOutside0X128: BigNumber;\n    feeGrowthOutside1X128: BigNumber;\n    tickCumulativeOutside: BigNumber;\n    secondsPerLiquidityOutsideX128: BigNumber;\n    secondsOutside: number;\n    initialized: boolean;\n    0: BigNumber;\n    1: BigNumber;\n    2: BigNumber;\n    3: BigNumber;\n    4: BigNumber;\n    5: BigNumber;\n    6: number;\n    7: boolean;\n  }> {\n    return new Promise((resolve) => {});\n  }\n\n  time(): Promise<BigNumber> {\n    return new Promise((resolve) => {});\n  }\n\n  token0(): Promise<string> {\n    return new Promise((resolve) => {});\n  }\n\n  token1(): Promise<string> {\n    return new Promise((resolve) => {});\n  }\n}\n"
  },
  {
    "path": "test/stubs/TestERC20.d.ts",
    "content": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n  ethers,\n  EventFilter,\n  Signer,\n  BigNumber,\n  BigNumberish,\n  PopulatedTransaction,\n} from \"ethers\";\nimport {\n  Contract,\n  ContractTransaction,\n  Overrides,\n  CallOverrides,\n} from \"@ethersproject/contracts\";\nimport { BytesLike } from \"@ethersproject/bytes\";\nimport { Listener, Provider } from \"@ethersproject/providers\";\nimport { FunctionFragment, EventFragment, Result } from \"@ethersproject/abi\";\n\ninterface TestERC20Interface extends ethers.utils.Interface {\n  functions: {\n    \"allowance(address,address)\": FunctionFragment;\n    \"approve(address,uint256)\": FunctionFragment;\n    \"balanceOf(address)\": FunctionFragment;\n    \"mint(address,uint256)\": FunctionFragment;\n    \"transfer(address,uint256)\": FunctionFragment;\n    \"transferFrom(address,address,uint256)\": FunctionFragment;\n  };\n\n  encodeFunctionData(\n    functionFragment: \"allowance\",\n    values: [string, string]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"approve\",\n    values: [string, BigNumberish]\n  ): string;\n  encodeFunctionData(functionFragment: \"balanceOf\", values: [string]): string;\n  encodeFunctionData(\n    functionFragment: \"mint\",\n    values: [string, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"transfer\",\n    values: [string, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"transferFrom\",\n    values: [string, string, BigNumberish]\n  ): string;\n\n  decodeFunctionResult(functionFragment: \"allowance\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"approve\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"balanceOf\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"mint\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"transfer\", data: BytesLike): Result;\n  decodeFunctionResult(\n    functionFragment: \"transferFrom\",\n    data: BytesLike\n  ): Result;\n\n  events: {\n    \"Approval(address,address,uint256)\": EventFragment;\n    \"Transfer(address,address,uint256)\": EventFragment;\n  };\n\n  getEvent(nameOrSignatureOrTopic: \"Approval\"): EventFragment;\n  getEvent(nameOrSignatureOrTopic: \"Transfer\"): EventFragment;\n}\n\nexport class TestERC20 extends Contract {\n  connect(signerOrProvider: Signer | Provider | string): this;\n  attach(addressOrName: string): this;\n  deployed(): Promise<this>;\n\n  on(event: EventFilter | string, listener: Listener): this;\n  once(event: EventFilter | string, listener: Listener): this;\n  addListener(eventName: EventFilter | string, listener: Listener): this;\n  removeAllListeners(eventName: EventFilter | string): this;\n  removeListener(eventName: any, listener: Listener): this;\n\n  interface: TestERC20Interface;\n\n  functions: {\n    allowance(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: BigNumber;\n    }>;\n\n    \"allowance(address,address)\"(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: BigNumber;\n    }>;\n\n    approve(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"approve(address,uint256)\"(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    balanceOf(\n      arg0: string,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: BigNumber;\n    }>;\n\n    \"balanceOf(address)\"(\n      arg0: string,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: BigNumber;\n    }>;\n\n    mint(\n      to: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"mint(address,uint256)\"(\n      to: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    transfer(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"transfer(address,uint256)\"(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    transferFrom(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"transferFrom(address,address,uint256)\"(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n  };\n\n  allowance(\n    arg0: string,\n    arg1: string,\n    overrides?: CallOverrides\n  ): Promise<BigNumber>;\n\n  \"allowance(address,address)\"(\n    arg0: string,\n    arg1: string,\n    overrides?: CallOverrides\n  ): Promise<BigNumber>;\n\n  approve(\n    spender: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"approve(address,uint256)\"(\n    spender: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  balanceOf(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;\n\n  \"balanceOf(address)\"(\n    arg0: string,\n    overrides?: CallOverrides\n  ): Promise<BigNumber>;\n\n  mint(\n    to: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"mint(address,uint256)\"(\n    to: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  transfer(\n    recipient: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"transfer(address,uint256)\"(\n    recipient: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  transferFrom(\n    sender: string,\n    recipient: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"transferFrom(address,address,uint256)\"(\n    sender: string,\n    recipient: string,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  callStatic: {\n    allowance(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    \"allowance(address,address)\"(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    approve(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<boolean>;\n\n    \"approve(address,uint256)\"(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<boolean>;\n\n    balanceOf(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;\n\n    \"balanceOf(address)\"(\n      arg0: string,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    mint(\n      to: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"mint(address,uint256)\"(\n      to: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    transfer(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<boolean>;\n\n    \"transfer(address,uint256)\"(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<boolean>;\n\n    transferFrom(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<boolean>;\n\n    \"transferFrom(address,address,uint256)\"(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<boolean>;\n  };\n\n  filters: {\n    Approval(\n      owner: string | null,\n      spender: string | null,\n      value: null\n    ): EventFilter;\n\n    Transfer(from: string | null, to: string | null, value: null): EventFilter;\n  };\n\n  estimateGas: {\n    allowance(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    \"allowance(address,address)\"(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    approve(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"approve(address,uint256)\"(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    balanceOf(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;\n\n    \"balanceOf(address)\"(\n      arg0: string,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    mint(\n      to: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"mint(address,uint256)\"(\n      to: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    transfer(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"transfer(address,uint256)\"(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    transferFrom(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"transferFrom(address,address,uint256)\"(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n  };\n\n  populateTransaction: {\n    allowance(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    \"allowance(address,address)\"(\n      arg0: string,\n      arg1: string,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    approve(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"approve(address,uint256)\"(\n      spender: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    balanceOf(\n      arg0: string,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    \"balanceOf(address)\"(\n      arg0: string,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    mint(\n      to: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"mint(address,uint256)\"(\n      to: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    transfer(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"transfer(address,uint256)\"(\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    transferFrom(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"transferFrom(address,address,uint256)\"(\n      sender: string,\n      recipient: string,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n  };\n}\n"
  },
  {
    "path": "test/stubs/TestUniswapV3Callee.d.ts",
    "content": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n  ethers,\n  EventFilter,\n  Signer,\n  BigNumber,\n  BigNumberish,\n  PopulatedTransaction,\n} from \"ethers\";\nimport {\n  Contract,\n  ContractTransaction,\n  Overrides,\n  CallOverrides,\n} from \"@ethersproject/contracts\";\nimport { BytesLike } from \"@ethersproject/bytes\";\nimport { Listener, Provider } from \"@ethersproject/providers\";\nimport { FunctionFragment, EventFragment, Result } from \"@ethersproject/abi\";\n\ninterface TestUniswapV3CalleeInterface extends ethers.utils.Interface {\n  functions: {\n    \"flash(address,address,uint256,uint256,uint256,uint256)\": FunctionFragment;\n    \"mint(address,address,int24,int24,uint128)\": FunctionFragment;\n    \"swap0ForExact1(address,uint256,address,uint160)\": FunctionFragment;\n    \"swap1ForExact0(address,uint256,address,uint160)\": FunctionFragment;\n    \"swapExact0For1(address,uint256,address,uint160)\": FunctionFragment;\n    \"swapExact1For0(address,uint256,address,uint160)\": FunctionFragment;\n    \"swapToHigherSqrtPrice(address,uint160,address)\": FunctionFragment;\n    \"swapToLowerSqrtPrice(address,uint160,address)\": FunctionFragment;\n    \"uniswapV3FlashCallback(uint256,uint256,bytes)\": FunctionFragment;\n    \"uniswapV3MintCallback(uint256,uint256,bytes)\": FunctionFragment;\n    \"uniswapV3SwapCallback(int256,int256,bytes)\": FunctionFragment;\n  };\n\n  encodeFunctionData(\n    functionFragment: \"flash\",\n    values: [\n      string,\n      string,\n      BigNumberish,\n      BigNumberish,\n      BigNumberish,\n      BigNumberish\n    ]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"mint\",\n    values: [string, string, BigNumberish, BigNumberish, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"swap0ForExact1\",\n    values: [string, BigNumberish, string, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"swap1ForExact0\",\n    values: [string, BigNumberish, string, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"swapExact0For1\",\n    values: [string, BigNumberish, string, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"swapExact1For0\",\n    values: [string, BigNumberish, string, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"swapToHigherSqrtPrice\",\n    values: [string, BigNumberish, string]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"swapToLowerSqrtPrice\",\n    values: [string, BigNumberish, string]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"uniswapV3FlashCallback\",\n    values: [BigNumberish, BigNumberish, BytesLike]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"uniswapV3MintCallback\",\n    values: [BigNumberish, BigNumberish, BytesLike]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"uniswapV3SwapCallback\",\n    values: [BigNumberish, BigNumberish, BytesLike]\n  ): string;\n\n  decodeFunctionResult(functionFragment: \"flash\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"mint\", data: BytesLike): Result;\n  decodeFunctionResult(\n    functionFragment: \"swap0ForExact1\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"swap1ForExact0\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"swapExact0For1\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"swapExact1For0\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"swapToHigherSqrtPrice\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"swapToLowerSqrtPrice\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"uniswapV3FlashCallback\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"uniswapV3MintCallback\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"uniswapV3SwapCallback\",\n    data: BytesLike\n  ): Result;\n\n  events: {\n    \"FlashCallback(uint256,uint256)\": EventFragment;\n    \"MintCallback(uint256,uint256)\": EventFragment;\n    \"SwapCallback(int256,int256)\": EventFragment;\n  };\n\n  getEvent(nameOrSignatureOrTopic: \"FlashCallback\"): EventFragment;\n  getEvent(nameOrSignatureOrTopic: \"MintCallback\"): EventFragment;\n  getEvent(nameOrSignatureOrTopic: \"SwapCallback\"): EventFragment;\n}\n\nexport class TestUniswapV3Callee extends Contract {\n  connect(signerOrProvider: Signer | Provider | string): this;\n  attach(addressOrName: string): this;\n  deployed(): Promise<this>;\n\n  on(event: EventFilter | string, listener: Listener): this;\n  once(event: EventFilter | string, listener: Listener): this;\n  addListener(eventName: EventFilter | string, listener: Listener): this;\n  removeAllListeners(eventName: EventFilter | string): this;\n  removeListener(eventName: any, listener: Listener): this;\n\n  interface: TestUniswapV3CalleeInterface;\n\n  functions: {\n    flash(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"flash(address,address,uint256,uint256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    mint(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"mint(address,address,int24,int24,uint128)\"(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    swap0ForExact1(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swap0ForExact1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    swap1ForExact0(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swap1ForExact0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    swapExact0For1(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swapExact0For1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    swapExact1For0(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swapExact1For0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    swapToHigherSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swapToHigherSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    swapToLowerSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swapToLowerSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    uniswapV3FlashCallback(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"uniswapV3FlashCallback(uint256,uint256,bytes)\"(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    uniswapV3MintCallback(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"uniswapV3MintCallback(uint256,uint256,bytes)\"(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    uniswapV3SwapCallback(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n  };\n\n  flash(\n    pool: string,\n    recipient: string,\n    amount0: BigNumberish,\n    amount1: BigNumberish,\n    pay0: BigNumberish,\n    pay1: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"flash(address,address,uint256,uint256,uint256,uint256)\"(\n    pool: string,\n    recipient: string,\n    amount0: BigNumberish,\n    amount1: BigNumberish,\n    pay0: BigNumberish,\n    pay1: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  mint(\n    pool: string,\n    recipient: string,\n    tickLower: BigNumberish,\n    tickUpper: BigNumberish,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"mint(address,address,int24,int24,uint128)\"(\n    pool: string,\n    recipient: string,\n    tickLower: BigNumberish,\n    tickUpper: BigNumberish,\n    amount: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  swap0ForExact1(\n    pool: string,\n    amount1Out: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swap0ForExact1(address,uint256,address,uint160)\"(\n    pool: string,\n    amount1Out: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  swap1ForExact0(\n    pool: string,\n    amount0Out: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swap1ForExact0(address,uint256,address,uint160)\"(\n    pool: string,\n    amount0Out: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  swapExact0For1(\n    pool: string,\n    amount0In: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swapExact0For1(address,uint256,address,uint160)\"(\n    pool: string,\n    amount0In: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  swapExact1For0(\n    pool: string,\n    amount1In: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swapExact1For0(address,uint256,address,uint160)\"(\n    pool: string,\n    amount1In: BigNumberish,\n    recipient: string,\n    sqrtPriceLimitX96: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  swapToHigherSqrtPrice(\n    pool: string,\n    sqrtPriceX96: BigNumberish,\n    recipient: string,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swapToHigherSqrtPrice(address,uint160,address)\"(\n    pool: string,\n    sqrtPriceX96: BigNumberish,\n    recipient: string,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  swapToLowerSqrtPrice(\n    pool: string,\n    sqrtPriceX96: BigNumberish,\n    recipient: string,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swapToLowerSqrtPrice(address,uint160,address)\"(\n    pool: string,\n    sqrtPriceX96: BigNumberish,\n    recipient: string,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  uniswapV3FlashCallback(\n    fee0: BigNumberish,\n    fee1: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"uniswapV3FlashCallback(uint256,uint256,bytes)\"(\n    fee0: BigNumberish,\n    fee1: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  uniswapV3MintCallback(\n    amount0Owed: BigNumberish,\n    amount1Owed: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"uniswapV3MintCallback(uint256,uint256,bytes)\"(\n    amount0Owed: BigNumberish,\n    amount1Owed: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  uniswapV3SwapCallback(\n    amount0Delta: BigNumberish,\n    amount1Delta: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n    amount0Delta: BigNumberish,\n    amount1Delta: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  callStatic: {\n    flash(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"flash(address,address,uint256,uint256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    mint(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"mint(address,address,int24,int24,uint128)\"(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    swap0ForExact1(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"swap0ForExact1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    swap1ForExact0(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"swap1ForExact0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    swapExact0For1(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"swapExact0For1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    swapExact1For0(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"swapExact1For0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    swapToHigherSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"swapToHigherSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    swapToLowerSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"swapToLowerSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    uniswapV3FlashCallback(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"uniswapV3FlashCallback(uint256,uint256,bytes)\"(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    uniswapV3MintCallback(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"uniswapV3MintCallback(uint256,uint256,bytes)\"(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    uniswapV3SwapCallback(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n  };\n\n  filters: {\n    FlashCallback(fee0: null, fee1: null): EventFilter;\n\n    MintCallback(amount0Owed: null, amount1Owed: null): EventFilter;\n\n    SwapCallback(amount0Delta: null, amount1Delta: null): EventFilter;\n  };\n\n  estimateGas: {\n    flash(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"flash(address,address,uint256,uint256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    mint(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"mint(address,address,int24,int24,uint128)\"(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    swap0ForExact1(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"swap0ForExact1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    swap1ForExact0(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"swap1ForExact0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    swapExact0For1(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"swapExact0For1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    swapExact1For0(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"swapExact1For0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    swapToHigherSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"swapToHigherSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    swapToLowerSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"swapToLowerSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    uniswapV3FlashCallback(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"uniswapV3FlashCallback(uint256,uint256,bytes)\"(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    uniswapV3MintCallback(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"uniswapV3MintCallback(uint256,uint256,bytes)\"(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    uniswapV3SwapCallback(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n  };\n\n  populateTransaction: {\n    flash(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"flash(address,address,uint256,uint256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      amount0: BigNumberish,\n      amount1: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    mint(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"mint(address,address,int24,int24,uint128)\"(\n      pool: string,\n      recipient: string,\n      tickLower: BigNumberish,\n      tickUpper: BigNumberish,\n      amount: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    swap0ForExact1(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swap0ForExact1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    swap1ForExact0(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swap1ForExact0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0Out: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    swapExact0For1(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swapExact0For1(address,uint256,address,uint160)\"(\n      pool: string,\n      amount0In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    swapExact1For0(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swapExact1For0(address,uint256,address,uint160)\"(\n      pool: string,\n      amount1In: BigNumberish,\n      recipient: string,\n      sqrtPriceLimitX96: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    swapToHigherSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swapToHigherSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    swapToLowerSqrtPrice(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swapToLowerSqrtPrice(address,uint160,address)\"(\n      pool: string,\n      sqrtPriceX96: BigNumberish,\n      recipient: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    uniswapV3FlashCallback(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"uniswapV3FlashCallback(uint256,uint256,bytes)\"(\n      fee0: BigNumberish,\n      fee1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    uniswapV3MintCallback(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"uniswapV3MintCallback(uint256,uint256,bytes)\"(\n      amount0Owed: BigNumberish,\n      amount1Owed: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    uniswapV3SwapCallback(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      amount0Delta: BigNumberish,\n      amount1Delta: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n  };\n}\n"
  },
  {
    "path": "test/stubs/TestUniswapV3ReentrantCallee.d.ts",
    "content": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n  ethers,\n  EventFilter,\n  Signer,\n  BigNumber,\n  BigNumberish,\n  PopulatedTransaction,\n} from \"ethers\";\nimport {\n  Contract,\n  ContractTransaction,\n  Overrides,\n  CallOverrides,\n} from \"@ethersproject/contracts\";\nimport { BytesLike } from \"@ethersproject/bytes\";\nimport { Listener, Provider } from \"@ethersproject/providers\";\nimport { FunctionFragment, EventFragment, Result } from \"@ethersproject/abi\";\n\ninterface TestUniswapV3ReentrantCalleeInterface extends ethers.utils.Interface {\n  functions: {\n    \"swapToReenter(address)\": FunctionFragment;\n    \"uniswapV3SwapCallback(int256,int256,bytes)\": FunctionFragment;\n  };\n\n  encodeFunctionData(\n    functionFragment: \"swapToReenter\",\n    values: [string]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"uniswapV3SwapCallback\",\n    values: [BigNumberish, BigNumberish, BytesLike]\n  ): string;\n\n  decodeFunctionResult(\n    functionFragment: \"swapToReenter\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"uniswapV3SwapCallback\",\n    data: BytesLike\n  ): Result;\n\n  events: {};\n}\n\nexport class TestUniswapV3ReentrantCallee extends Contract {\n  connect(signerOrProvider: Signer | Provider | string): this;\n  attach(addressOrName: string): this;\n  deployed(): Promise<this>;\n\n  on(event: EventFilter | string, listener: Listener): this;\n  once(event: EventFilter | string, listener: Listener): this;\n  addListener(eventName: EventFilter | string, listener: Listener): this;\n  removeAllListeners(eventName: EventFilter | string): this;\n  removeListener(eventName: any, listener: Listener): this;\n\n  interface: TestUniswapV3ReentrantCalleeInterface;\n\n  functions: {\n    swapToReenter(\n      pool: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swapToReenter(address)\"(\n      pool: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n  };\n\n  swapToReenter(\n    pool: string,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swapToReenter(address)\"(\n    pool: string,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  uniswapV3SwapCallback(\n    arg0: BigNumberish,\n    arg1: BigNumberish,\n    arg2: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n    arg0: BigNumberish,\n    arg1: BigNumberish,\n    arg2: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  callStatic: {\n    swapToReenter(pool: string, overrides?: CallOverrides): Promise<void>;\n\n    \"swapToReenter(address)\"(\n      pool: string,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n  };\n\n  filters: {};\n\n  estimateGas: {\n    swapToReenter(pool: string, overrides?: Overrides): Promise<BigNumber>;\n\n    \"swapToReenter(address)\"(\n      pool: string,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n  };\n\n  populateTransaction: {\n    swapToReenter(\n      pool: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swapToReenter(address)\"(\n      pool: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      arg2: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n  };\n}\n"
  },
  {
    "path": "test/stubs/TestUniswapV3SwapPay.d.ts",
    "content": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n  ethers,\n  EventFilter,\n  Signer,\n  BigNumber,\n  BigNumberish,\n  PopulatedTransaction,\n} from \"ethers\";\nimport {\n  Contract,\n  ContractTransaction,\n  Overrides,\n  CallOverrides,\n} from \"@ethersproject/contracts\";\nimport { BytesLike } from \"@ethersproject/bytes\";\nimport { Listener, Provider } from \"@ethersproject/providers\";\nimport { FunctionFragment, EventFragment, Result } from \"@ethersproject/abi\";\n\ninterface TestUniswapV3SwapPayInterface extends ethers.utils.Interface {\n  functions: {\n    \"swap(address,address,bool,uint160,int256,uint256,uint256)\": FunctionFragment;\n    \"uniswapV3SwapCallback(int256,int256,bytes)\": FunctionFragment;\n  };\n\n  encodeFunctionData(\n    functionFragment: \"swap\",\n    values: [\n      string,\n      string,\n      boolean,\n      BigNumberish,\n      BigNumberish,\n      BigNumberish,\n      BigNumberish\n    ]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"uniswapV3SwapCallback\",\n    values: [BigNumberish, BigNumberish, BytesLike]\n  ): string;\n\n  decodeFunctionResult(functionFragment: \"swap\", data: BytesLike): Result;\n  decodeFunctionResult(\n    functionFragment: \"uniswapV3SwapCallback\",\n    data: BytesLike\n  ): Result;\n\n  events: {};\n}\n\nexport class TestUniswapV3SwapPay extends Contract {\n  connect(signerOrProvider: Signer | Provider | string): this;\n  attach(addressOrName: string): this;\n  deployed(): Promise<this>;\n\n  on(event: EventFilter | string, listener: Listener): this;\n  once(event: EventFilter | string, listener: Listener): this;\n  addListener(eventName: EventFilter | string, listener: Listener): this;\n  removeAllListeners(eventName: EventFilter | string): this;\n  removeListener(eventName: any, listener: Listener): this;\n\n  interface: TestUniswapV3SwapPayInterface;\n\n  functions: {\n    swap(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"swap(address,address,bool,uint160,int256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n  };\n\n  swap(\n    pool: string,\n    recipient: string,\n    zeroForOne: boolean,\n    sqrtPriceX96: BigNumberish,\n    amountSpecified: BigNumberish,\n    pay0: BigNumberish,\n    pay1: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"swap(address,address,bool,uint160,int256,uint256,uint256)\"(\n    pool: string,\n    recipient: string,\n    zeroForOne: boolean,\n    sqrtPriceX96: BigNumberish,\n    amountSpecified: BigNumberish,\n    pay0: BigNumberish,\n    pay1: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  uniswapV3SwapCallback(\n    arg0: BigNumberish,\n    arg1: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n    arg0: BigNumberish,\n    arg1: BigNumberish,\n    data: BytesLike,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  callStatic: {\n    swap(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"swap(address,address,bool,uint160,int256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: CallOverrides\n    ): Promise<void>;\n  };\n\n  filters: {};\n\n  estimateGas: {\n    swap(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"swap(address,address,bool,uint160,int256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n  };\n\n  populateTransaction: {\n    swap(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"swap(address,address,bool,uint160,int256,uint256,uint256)\"(\n      pool: string,\n      recipient: string,\n      zeroForOne: boolean,\n      sqrtPriceX96: BigNumberish,\n      amountSpecified: BigNumberish,\n      pay0: BigNumberish,\n      pay1: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    uniswapV3SwapCallback(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"uniswapV3SwapCallback(int256,int256,bytes)\"(\n      arg0: BigNumberish,\n      arg1: BigNumberish,\n      data: BytesLike,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n  };\n}\n"
  },
  {
    "path": "test/stubs/TickTest.ts",
    "content": "import { TickManager } from \"../../src/manager/TickManager\";\nimport { TickMath } from \"../../src/util/TickMath\";\nimport JSBI from \"jsbi\";\nimport { MockableTick } from \"../shared/MockableTick\";\n\nexport class TickTest {\n  private manager: TickManager;\n  constructor(manager: TickManager) {\n    this.manager = manager;\n  }\n  clear(tick: number): Promise<void> {\n    this.manager.clear(tick);\n    return Promise.resolve();\n  }\n\n  cross(\n    tick: number,\n    feeGrowthGlobal0X128: JSBI,\n    feeGrowthGlobal1X128: JSBI\n  ): Promise<JSBI> {\n    const tickToCross = this.manager.getTickAndInitIfAbsent(tick);\n    return Promise.resolve(\n      tickToCross.cross(feeGrowthGlobal0X128, feeGrowthGlobal1X128)\n    );\n  }\n\n  getFeeGrowthInside(\n    tickLower: number,\n    tickUpper: number,\n    tickCurrent: number,\n    feeGrowthGlobal0X128: JSBI,\n    feeGrowthGlobal1X128: JSBI\n  ): Promise<{\n    feeGrowthInside0X128: JSBI;\n    feeGrowthInside1X128: JSBI;\n  }> {\n    return Promise.resolve(\n      this.manager.getFeeGrowthInside(\n        tickLower,\n        tickUpper,\n        tickCurrent,\n        feeGrowthGlobal0X128,\n        feeGrowthGlobal1X128\n      )\n    );\n  }\n\n  setTick(\n    tick: number,\n    info: {\n      liquidityGross: JSBI;\n      liquidityNet: JSBI;\n      feeGrowthOutside0X128: JSBI;\n      feeGrowthOutside1X128: JSBI;\n    }\n  ): Promise<MockableTick> {\n    const newTick = new MockableTick(tick);\n    newTick.updateProperties(\n      info.liquidityGross,\n      info.liquidityNet,\n      info.feeGrowthOutside0X128,\n      info.feeGrowthOutside1X128\n    );\n    this.manager.set(newTick);\n    return Promise.resolve(newTick);\n  }\n\n  tickSpacingToMaxLiquidityPerTick(tickSpacing: number): Promise<JSBI> {\n    return Promise.resolve(\n      TickMath.tickSpacingToMaxLiquidityPerTick(tickSpacing)\n    );\n  }\n\n  ticks(tickIndex: number): Promise<MockableTick> {\n    return Promise.resolve(\n      this.manager.getTickAndInitIfAbsent(tickIndex) as MockableTick\n    );\n  }\n\n  update(\n    tick: number,\n    tickCurrent: number,\n    liquidityDelta: JSBI,\n    feeGrowthGlobal0X128: JSBI,\n    feeGrowthGlobal1X128: JSBI,\n    upper: boolean,\n    maxLiquidity: JSBI\n  ): Promise<boolean> {\n    const tickToUpdate = this.manager.getTickAndInitIfAbsent(tick);\n    return Promise.resolve(\n      tickToUpdate.update(\n        liquidityDelta,\n        tickCurrent,\n        feeGrowthGlobal0X128,\n        feeGrowthGlobal1X128,\n        upper,\n        maxLiquidity\n      )\n    );\n  }\n}\n"
  },
  {
    "path": "test/stubs/UniswapV3Factory.d.ts",
    "content": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n  ethers,\n  EventFilter,\n  Signer,\n  BigNumber,\n  BigNumberish,\n  PopulatedTransaction,\n} from \"ethers\";\nimport {\n  Contract,\n  ContractTransaction,\n  Overrides,\n  CallOverrides,\n} from \"@ethersproject/contracts\";\nimport { BytesLike } from \"@ethersproject/bytes\";\nimport { Listener, Provider } from \"@ethersproject/providers\";\nimport { FunctionFragment, EventFragment, Result } from \"@ethersproject/abi\";\n\ninterface UniswapV3FactoryInterface extends ethers.utils.Interface {\n  functions: {\n    \"createPool(address,address,uint24)\": FunctionFragment;\n    \"enableFeeAmount(uint24,int24)\": FunctionFragment;\n    \"feeAmountTickSpacing(uint24)\": FunctionFragment;\n    \"getPool(address,address,uint24)\": FunctionFragment;\n    \"owner()\": FunctionFragment;\n    \"parameters()\": FunctionFragment;\n    \"setOwner(address)\": FunctionFragment;\n  };\n\n  encodeFunctionData(\n    functionFragment: \"createPool\",\n    values: [string, string, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"enableFeeAmount\",\n    values: [BigNumberish, BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"feeAmountTickSpacing\",\n    values: [BigNumberish]\n  ): string;\n  encodeFunctionData(\n    functionFragment: \"getPool\",\n    values: [string, string, BigNumberish]\n  ): string;\n  encodeFunctionData(functionFragment: \"owner\", values?: undefined): string;\n  encodeFunctionData(\n    functionFragment: \"parameters\",\n    values?: undefined\n  ): string;\n  encodeFunctionData(functionFragment: \"setOwner\", values: [string]): string;\n\n  decodeFunctionResult(functionFragment: \"createPool\", data: BytesLike): Result;\n  decodeFunctionResult(\n    functionFragment: \"enableFeeAmount\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(\n    functionFragment: \"feeAmountTickSpacing\",\n    data: BytesLike\n  ): Result;\n  decodeFunctionResult(functionFragment: \"getPool\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"owner\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"parameters\", data: BytesLike): Result;\n  decodeFunctionResult(functionFragment: \"setOwner\", data: BytesLike): Result;\n\n  events: {\n    \"FeeAmountEnabled(uint24,int24)\": EventFragment;\n    \"OwnerChanged(address,address)\": EventFragment;\n    \"PoolCreated(address,address,uint24,int24,address)\": EventFragment;\n  };\n\n  getEvent(nameOrSignatureOrTopic: \"FeeAmountEnabled\"): EventFragment;\n  getEvent(nameOrSignatureOrTopic: \"OwnerChanged\"): EventFragment;\n  getEvent(nameOrSignatureOrTopic: \"PoolCreated\"): EventFragment;\n}\n\nexport class UniswapV3Factory extends Contract {\n  connect(signerOrProvider: Signer | Provider | string): this;\n  attach(addressOrName: string): this;\n  deployed(): Promise<this>;\n\n  on(event: EventFilter | string, listener: Listener): this;\n  once(event: EventFilter | string, listener: Listener): this;\n  addListener(eventName: EventFilter | string, listener: Listener): this;\n  removeAllListeners(eventName: EventFilter | string): this;\n  removeListener(eventName: any, listener: Listener): this;\n\n  interface: UniswapV3FactoryInterface;\n\n  functions: {\n    createPool(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"createPool(address,address,uint24)\"(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    enableFeeAmount(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"enableFeeAmount(uint24,int24)\"(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    feeAmountTickSpacing(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: number;\n    }>;\n\n    \"feeAmountTickSpacing(uint24)\"(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: number;\n    }>;\n\n    getPool(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: string;\n    }>;\n\n    \"getPool(address,address,uint24)\"(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<{\n      0: string;\n    }>;\n\n    owner(overrides?: CallOverrides): Promise<{\n      0: string;\n    }>;\n\n    \"owner()\"(overrides?: CallOverrides): Promise<{\n      0: string;\n    }>;\n\n    parameters(overrides?: CallOverrides): Promise<{\n      factory: string;\n      token0: string;\n      token1: string;\n      fee: number;\n      tickSpacing: number;\n      0: string;\n      1: string;\n      2: string;\n      3: number;\n      4: number;\n    }>;\n\n    \"parameters()\"(overrides?: CallOverrides): Promise<{\n      factory: string;\n      token0: string;\n      token1: string;\n      fee: number;\n      tickSpacing: number;\n      0: string;\n      1: string;\n      2: string;\n      3: number;\n      4: number;\n    }>;\n\n    setOwner(\n      _owner: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n\n    \"setOwner(address)\"(\n      _owner: string,\n      overrides?: Overrides\n    ): Promise<ContractTransaction>;\n  };\n\n  createPool(\n    tokenA: string,\n    tokenB: string,\n    fee: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"createPool(address,address,uint24)\"(\n    tokenA: string,\n    tokenB: string,\n    fee: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  enableFeeAmount(\n    fee: BigNumberish,\n    tickSpacing: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  \"enableFeeAmount(uint24,int24)\"(\n    fee: BigNumberish,\n    tickSpacing: BigNumberish,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  feeAmountTickSpacing(\n    arg0: BigNumberish,\n    overrides?: CallOverrides\n  ): Promise<number>;\n\n  \"feeAmountTickSpacing(uint24)\"(\n    arg0: BigNumberish,\n    overrides?: CallOverrides\n  ): Promise<number>;\n\n  getPool(\n    arg0: string,\n    arg1: string,\n    arg2: BigNumberish,\n    overrides?: CallOverrides\n  ): Promise<string>;\n\n  \"getPool(address,address,uint24)\"(\n    arg0: string,\n    arg1: string,\n    arg2: BigNumberish,\n    overrides?: CallOverrides\n  ): Promise<string>;\n\n  owner(overrides?: CallOverrides): Promise<string>;\n\n  \"owner()\"(overrides?: CallOverrides): Promise<string>;\n\n  parameters(overrides?: CallOverrides): Promise<{\n    factory: string;\n    token0: string;\n    token1: string;\n    fee: number;\n    tickSpacing: number;\n    0: string;\n    1: string;\n    2: string;\n    3: number;\n    4: number;\n  }>;\n\n  \"parameters()\"(overrides?: CallOverrides): Promise<{\n    factory: string;\n    token0: string;\n    token1: string;\n    fee: number;\n    tickSpacing: number;\n    0: string;\n    1: string;\n    2: string;\n    3: number;\n    4: number;\n  }>;\n\n  setOwner(_owner: string, overrides?: Overrides): Promise<ContractTransaction>;\n\n  \"setOwner(address)\"(\n    _owner: string,\n    overrides?: Overrides\n  ): Promise<ContractTransaction>;\n\n  callStatic: {\n    createPool(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<string>;\n\n    \"createPool(address,address,uint24)\"(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<string>;\n\n    enableFeeAmount(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    \"enableFeeAmount(uint24,int24)\"(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<void>;\n\n    feeAmountTickSpacing(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<number>;\n\n    \"feeAmountTickSpacing(uint24)\"(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<number>;\n\n    getPool(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<string>;\n\n    \"getPool(address,address,uint24)\"(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<string>;\n\n    owner(overrides?: CallOverrides): Promise<string>;\n\n    \"owner()\"(overrides?: CallOverrides): Promise<string>;\n\n    parameters(overrides?: CallOverrides): Promise<{\n      factory: string;\n      token0: string;\n      token1: string;\n      fee: number;\n      tickSpacing: number;\n      0: string;\n      1: string;\n      2: string;\n      3: number;\n      4: number;\n    }>;\n\n    \"parameters()\"(overrides?: CallOverrides): Promise<{\n      factory: string;\n      token0: string;\n      token1: string;\n      fee: number;\n      tickSpacing: number;\n      0: string;\n      1: string;\n      2: string;\n      3: number;\n      4: number;\n    }>;\n\n    setOwner(_owner: string, overrides?: CallOverrides): Promise<void>;\n\n    \"setOwner(address)\"(\n      _owner: string,\n      overrides?: CallOverrides\n    ): Promise<void>;\n  };\n\n  filters: {\n    FeeAmountEnabled(\n      fee: BigNumberish | null,\n      tickSpacing: BigNumberish | null\n    ): EventFilter;\n\n    OwnerChanged(oldOwner: string | null, newOwner: string | null): EventFilter;\n\n    PoolCreated(\n      token0: string | null,\n      token1: string | null,\n      fee: BigNumberish | null,\n      tickSpacing: null,\n      pool: null\n    ): EventFilter;\n  };\n\n  estimateGas: {\n    createPool(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"createPool(address,address,uint24)\"(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    enableFeeAmount(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    \"enableFeeAmount(uint24,int24)\"(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n\n    feeAmountTickSpacing(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    \"feeAmountTickSpacing(uint24)\"(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    getPool(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    \"getPool(address,address,uint24)\"(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<BigNumber>;\n\n    owner(overrides?: CallOverrides): Promise<BigNumber>;\n\n    \"owner()\"(overrides?: CallOverrides): Promise<BigNumber>;\n\n    parameters(overrides?: CallOverrides): Promise<BigNumber>;\n\n    \"parameters()\"(overrides?: CallOverrides): Promise<BigNumber>;\n\n    setOwner(_owner: string, overrides?: Overrides): Promise<BigNumber>;\n\n    \"setOwner(address)\"(\n      _owner: string,\n      overrides?: Overrides\n    ): Promise<BigNumber>;\n  };\n\n  populateTransaction: {\n    createPool(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"createPool(address,address,uint24)\"(\n      tokenA: string,\n      tokenB: string,\n      fee: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    enableFeeAmount(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"enableFeeAmount(uint24,int24)\"(\n      fee: BigNumberish,\n      tickSpacing: BigNumberish,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    feeAmountTickSpacing(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    \"feeAmountTickSpacing(uint24)\"(\n      arg0: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    getPool(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    \"getPool(address,address,uint24)\"(\n      arg0: string,\n      arg1: string,\n      arg2: BigNumberish,\n      overrides?: CallOverrides\n    ): Promise<PopulatedTransaction>;\n\n    owner(overrides?: CallOverrides): Promise<PopulatedTransaction>;\n\n    \"owner()\"(overrides?: CallOverrides): Promise<PopulatedTransaction>;\n\n    parameters(overrides?: CallOverrides): Promise<PopulatedTransaction>;\n\n    \"parameters()\"(overrides?: CallOverrides): Promise<PopulatedTransaction>;\n\n    setOwner(\n      _owner: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n\n    \"setOwner(address)\"(\n      _owner: string,\n      overrides?: Overrides\n    ): Promise<PopulatedTransaction>;\n  };\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"target\": \"es2018\",\n    \"module\": \"commonjs\",\n    \"strict\": false,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noImplicitReturns\": true,\n    \"noUnusedLocals\": false,\n    \"esModuleInterop\": true,\n    \"declaration\": true,\n    \"rootDirs\": [\"./src\"],\n    \"outDir\": \"./dist\",\n    \"resolveJsonModule\": true,\n    \"allowJs\": true\n  },\n  \"exclude\": [\"dist\", \"node_modules\"],\n  \"include\": [\"./src\"]\n}\n"
  },
  {
    "path": "tuner.config.js",
    "content": "module.exports = {\n  RPCProviderUrl: process.env.MAINNET_PROVIDER_URL,\n};\n"
  }
]