Showing preview only (794K chars total). Download the full file or copy to clipboard to get everything.
Repository: Bella-DeFinTech/uniswap-v3-simulator
Branch: main
Commit: e7a44bfe13ab
Files: 164
Total size: 745.0 KB
Directory structure:
gitextract_2ntasox1/
├── .github/
│ └── workflows/
│ ├── lint-from-fork.yml
│ └── unit-tests-from-fork.yml
├── .gitignore
├── .prettierrc.json
├── LICENSE
├── README.md
├── SUMMARY.md
├── abi/
│ ├── OHM.json
│ ├── UniswapV3Pool2.json
│ └── Visor.json
├── docs/
│ ├── about-core-pool-config.md
│ ├── building-a-client-instance.md
│ ├── configuration.md
│ ├── contributing.md
│ ├── fetching-all-the-data-of-a-certain-pool-from-ethereum.md
│ ├── forking-and-retracing.md
│ ├── getting-a-core-pool-instance.md
│ ├── getting-a-pool-instance-with-the-data-fetched.md
│ ├── how-tuner-library-works.md
│ ├── installing-tuner.md
│ ├── interacting-with-core-pool.md
│ ├── loading-and-streaming-events-into-a-pool.md
│ ├── performance.md
│ ├── persisting-and-recovering.md
│ ├── pool-state-and-transition.md
│ ├── post-processor.md
│ ├── quick-start.md
│ └── simulator-roadmap-manager.md
├── examples/
│ ├── Uniswap-v3-Events-Downloader/
│ │ ├── EventsDownloader.ts
│ │ └── EventsUpdater.ts
│ └── Uniswap-v3-Strategy-Backtest/
│ └── README.md
├── hardhat.config.ts
├── package.json
├── src/
│ ├── client/
│ │ ├── BSCDataDownloader.ts
│ │ ├── SimulatorClient.ts
│ │ └── index.ts
│ ├── config/
│ │ └── TunerConfig.ts
│ ├── core/
│ │ ├── ConfigurableCorePool.ts
│ │ ├── CorePool.ts
│ │ └── index.ts
│ ├── entity/
│ │ ├── EndBlockType.ts
│ │ ├── LiquidityEvent.ts
│ │ ├── Record.ts
│ │ ├── Snapshot.ts
│ │ ├── SnapshotProfile.ts
│ │ ├── StepComputations.ts
│ │ ├── SwapEvent.ts
│ │ └── index.ts
│ ├── enum/
│ │ ├── ActionType.ts
│ │ ├── EventDataSourceType.ts
│ │ ├── EventType.ts
│ │ ├── FeeAmount.ts
│ │ ├── InternalConstants.ts
│ │ └── index.ts
│ ├── index.ts
│ ├── interface/
│ │ ├── ActionParams.ts
│ │ ├── ConfigurableCorePool.ts
│ │ ├── CorePoolView.ts
│ │ ├── PoolStateContainer.ts
│ │ ├── PoolStateView.ts
│ │ ├── PositionView.ts
│ │ ├── SimulationDataManager.ts
│ │ ├── SimulatorRoadmapManager.ts
│ │ ├── SimulatorVisitor.ts
│ │ ├── TickView.ts
│ │ ├── Transition.ts
│ │ ├── Visitable.ts
│ │ └── index.ts
│ ├── manager/
│ │ ├── EventDBManager.ts
│ │ ├── PositionManager.ts
│ │ ├── SQLiteSimulationDataManager.ts
│ │ ├── SimulatorConsoleVisitor.ts
│ │ ├── SimulatorPersistenceVisitor.ts
│ │ ├── SimulatorRoadmapManager.ts
│ │ ├── TickManager.ts
│ │ └── index.ts
│ ├── model/
│ │ ├── PoolConfig.ts
│ │ ├── PoolState.ts
│ │ ├── Position.ts
│ │ ├── Roadmap.ts
│ │ ├── Tick.ts
│ │ ├── Transition.ts
│ │ └── index.ts
│ └── util/
│ ├── BNUtils.ts
│ ├── DateConverter.ts
│ ├── DateUtils.ts
│ ├── FileUtils.ts
│ ├── FullMath.ts
│ ├── IdGenerator.ts
│ ├── LiquidityMath.ts
│ ├── PoolStateHelper.ts
│ ├── Serializer.ts
│ ├── SqrtPriceMath.ts
│ ├── SwapMath.ts
│ ├── TickMath.ts
│ └── index.ts
├── test/
│ ├── ConfigurableCorePool.test.ts
│ ├── JSBI.test.ts
│ ├── LiquidityMath.test.ts
│ ├── Serializer.test.ts
│ ├── SimulationDataManager.test.ts
│ ├── SimulatorClient.test.ts
│ ├── SimulatorRoadmapManager.test.ts
│ ├── SwapMath.spec.ts
│ ├── TestSubgraph.test.ts
│ ├── Tick.spec.ts
│ ├── TickManager.test.ts
│ ├── TickMath.test.ts
│ ├── UniswapV3Pool.spec.ts.pending
│ ├── UniswapV3Pool.swaps.spec.ts.pending
│ ├── contracts/
│ │ ├── CorePool.test.ts
│ │ ├── Ticks.test.ts
│ │ └── src/
│ │ └── contracts/
│ │ ├── NoDelegateCall.sol
│ │ ├── UniswapV3Factory2.sol
│ │ ├── UniswapV3Pool2.sol
│ │ ├── UniswapV3PoolDeployer2.sol
│ │ ├── interfaces/
│ │ │ ├── IERC20Minimal.sol
│ │ │ ├── IUniswapV3Factory.sol
│ │ │ ├── IUniswapV3Pool.sol
│ │ │ ├── IUniswapV3PoolDeployer.sol
│ │ │ ├── LICENSE
│ │ │ ├── callback/
│ │ │ │ ├── IUniswapV3FlashCallback.sol
│ │ │ │ ├── IUniswapV3MintCallback.sol
│ │ │ │ └── IUniswapV3SwapCallback.sol
│ │ │ └── pool/
│ │ │ ├── IUniswapV3PoolActions.sol
│ │ │ ├── IUniswapV3PoolDerivedState.sol
│ │ │ ├── IUniswapV3PoolEvents.sol
│ │ │ ├── IUniswapV3PoolImmutables.sol
│ │ │ ├── IUniswapV3PoolOwnerActions.sol
│ │ │ └── IUniswapV3PoolState.sol
│ │ └── libraries/
│ │ ├── BitMath.sol
│ │ ├── FixedPoint128.sol
│ │ ├── FixedPoint96.sol
│ │ ├── FullMath.sol
│ │ ├── LICENSE_GPL
│ │ ├── LICENSE_MIT
│ │ ├── LiquidityMath.sol
│ │ ├── LowGasSafeMath.sol
│ │ ├── Oracle.sol
│ │ ├── Position.sol
│ │ ├── SafeCast.sol
│ │ ├── SqrtPriceMath.sol
│ │ ├── SwapMath.sol
│ │ ├── Tick.sol
│ │ ├── TickBitmap.sol
│ │ ├── TickMath.sol
│ │ ├── TransferHelper.sol
│ │ └── UnsafeMath.sol
│ ├── shared/
│ │ ├── MockableTick.ts
│ │ ├── checkObservationEquals.ts
│ │ ├── expect.ts
│ │ ├── fixtures.ts
│ │ ├── format.ts
│ │ ├── snapshotGasCost.ts
│ │ └── utilities.ts
│ └── stubs/
│ ├── MockTimeUniswapV3Pool.ts
│ ├── TestERC20.d.ts
│ ├── TestUniswapV3Callee.d.ts
│ ├── TestUniswapV3ReentrantCallee.d.ts
│ ├── TestUniswapV3SwapPay.d.ts
│ ├── TickTest.ts
│ └── UniswapV3Factory.d.ts
├── tsconfig.json
└── tuner.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/lint-from-fork.yml
================================================
name: Lint On PR
on:
pull_request_target:
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v2
- name: Set up node
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run linters
uses: wearerequired/lint-action@v1
with:
github_token: ${{ secrets.github_token }}
prettier: true
prettier_extensions: 'js,json,md,ts,yaml,yml'
================================================
FILE: .github/workflows/unit-tests-from-fork.yml
================================================
name: Unit Tests On PR
on:
pull_request_target:
jobs:
approve:
runs-on: ubuntu-latest
steps:
- name: Approve
run: echo For security reasons, all pull requests need to be approved first before running any automated CI.
tests:
name: Unit tests
runs-on: ubuntu-latest
needs: [approve]
environment: test-env
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Generate typechain files from abi
run: yarn generate-types
- name: Run tests
env:
MAINNET_PROVIDER_URL: ${{ secrets.MAINNET_PROVIDER_URL }}
run: yarn test
================================================
FILE: .gitignore
================================================
# truffle build folder
build
# saddle build folder
.build/
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
coverage.json
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# deploy info file
deploy.txt
# slither requires a hardcoded absolute path for open-zeppelin libraries, so-called "remapping"
slither.config.json
# local vscode config
.vscode/
vaults.json
# --standard-json
solcinputjson.sh
output.json
# mocha test report
mochawesome-report/
#prettier ignore
.prettierignore
#hardhat
artifacts/
cache/
typechain/
================================================
FILE: .prettierrc.json
================================================
{}
================================================
FILE: LICENSE
================================================
Business Source License 1.1
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
"Business Source License" is a trademark of MariaDB Corporation Ab.
---
Parameters
Licensor: Uniswap Labs
Licensed Work: Uniswap V3 Core
The Licensed Work is (c) 2021 Uniswap Labs
Additional Use Grant: Any uses listed and defined at
v3-core-license-grants.uniswap.eth
Change Date: The earlier of 2023-04-01 or a date specified at
v3-core-license-date.uniswap.eth
Change License: GNU General Public License v2.0 or later
---
Terms
The Licensor hereby grants you the right to copy, modify, create derivative
works, redistribute, and make non-production use of the Licensed Work. The
Licensor may make an Additional Use Grant, above, permitting limited
production use.
Effective on the Change Date, or the fourth anniversary of the first publicly
available distribution of a specific version of the Licensed Work under this
License, whichever comes first, the Licensor hereby grants you rights under
the terms of the Change License, and the rights granted in the paragraph
above terminate.
If your use of the Licensed Work does not comply with the requirements
currently in effect as described in this License, you must purchase a
commercial license from the Licensor, its affiliated entities, or authorized
resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works
of the Licensed Work, are subject to this License. This License applies
separately for each version of the Licensed Work and the Change Date may vary
for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy
of the Licensed Work. If you receive the Licensed Work in original or
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically
terminate your rights under this License for the current and all other
versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of
Licensor or its affiliates (provided that you may use a trademark or logo of
Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.
MariaDB hereby grants you permission to use this License’s text to license
your works, and to refer to it using the trademark "Business Source License",
as long as you comply with the Covenants of Licensor below.
---
Covenants of Licensor
In consideration of the right to use this License’s text and the "Business
Source License" name and trademark, Licensor covenants to MariaDB, and to all
other recipients of the licensed work to be provided by Licensor:
1. To specify as the Change License the GPL Version 2.0 or any later version,
or a license that is compatible with GPL Version 2.0 or a later version,
where "compatible" means that software provided under the Change License can
be included in a program with software provided under GPL Version 2.0 or a
later version. Licensor may specify additional Change Licenses without
limitation.
2. To either: (a) specify an additional grant of rights to use that does not
impose any additional restriction on the right granted in this License, as
the Additional Use Grant; or (b) insert the text "None".
3. To specify a Change Date.
4. Not to modify this License in any other way.
---
Notice
The Business Source License (this document, or the "License") is not an Open
Source license. However, the Licensed Work will eventually be made available
under an Open Source License, as stated in this License.
================================================
FILE: README.md
================================================
[](https://twitter.com/BellaProtocol)
[](https://discord.gg/8ctd5geS8t)
# the "_Tuner_", a programmable, transaction-based Uniswap V3 Simulator with 100% Precision
> Before an orchestra, every musical instrument has to be _in-tune_, to ensure an outstanding performance.
>
> Before running a strategy, every parameter has to be _fine tuned_, to maximize the performance.
#### _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.
## Documentation
- [Getting Started](https://docs.bella.fi/getting-started)
- [Configuration](https://docs.bella.fi/configuration)
- [Guides](https://docs.bella.fi/guides)
- Developer Articles(stay tuned)
#### _Tuner_ is fundamentally a state machine, it can:
> **Completely replicate the tick-level calculation**
- this means your strategy will run through the Uniswap V3 implementation logic instead of just the high-level mathematic model.
> **Maintain the identical tick-level precision of prices, fees, and positions of Uniswap V3**
- this means the result of your backtesting is true to the real performance with the minimum margin of deviations.
> **Run fast**
- the EVM is slow, the historical dataset is huge, the Ganache cannot do the job, so use _Tuner_.
> **Fast-forward and rewind transactions**
- this means you can easily repeat a small portion of your test with a different set of parameters without the need to start over.
> **Take or recover from a snapshot(state)**
- this means you can run continuous regression test as your strategies constantly evolves.
> **Branch out and runs in parallel**
- this means you can run multiple back-tests each with a different set of parameters at the same time and compare the performance.
> **Persist historical data and strategy execution records in a SQLite database**
- this means the strategists can do advanced statistical analysis both in real-time and after the testing.
================================================
FILE: SUMMARY.md
================================================
# Table of contents
## Getting Started
- [Overview](README.md)
- [How "Tuner" Library Works?](docs/how-tuner-library-works.md)
- [Installing "Tuner"](docs/installing-tuner.md)
- [Quick Start](docs/quick-start.md)
## Configuration
- [Configuration](docs/configuration.md)
## Guides
- (Basic)For anyone who is interested in the Uniswap v3 model
- [Building a client instance](docs/building-a-client-instance.md)
- [About Core Pool Config](docs/about-core-pool-config.md)
- [Getting a Core Pool instance](docs/getting-a-core-pool-instance.md)
- [Interacting with Core Pool](docs/interacting-with-core-pool.md)
- (Typical)For a quant developer who works on a real pool on mainnet
- [Fetching all the data of a certain pool from Ethereum](docs/fetching-all-the-data-of-a-certain-pool-from-ethereum.md)
- [Getting a pool instance with the data fetched](docs/getting-a-pool-instance-with-the-data-fetched.md)
- [Loading and streaming events into a pool](docs/loading-and-streaming-events-into-a-pool.md)
- (Advanced)For a better user experience as a state machine
- [PoolState & Transition](docs/pool-state-and-transition.md)
- [Post-processor](docs/post-processor.md)
- [Forking & Retracing](docs/forking-and-retracing.md)
- [Persisting & Recovering](docs/persisting-and-recovering.md)
- [SimulatorRoadmapManager](docs/simulator-roadmap-manager.md)
## Performance
- [Performance](docs/performance.md)
## Examples
- [Uniswap-v3-Events-Downloader](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Events-Downloader)
- [Uniswap-v3-Strategy-Backtest](https://github.com/Bella-DeFinTech/uniswap-v3-simulator/tree/main/examples/Uniswap-v3-Strategy-Backtest)
- Uniswap-v3-Risk-Analysis(will be available soon)
## Contributing
- [Contributing](docs/contributing.md)
================================================
FILE: abi/OHM.json
================================================
[
{ "inputs": [], "stateMutability": "nonpayable", "type": "constructor" },
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "previousTWAPEpochPeriod",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newTWAPEpochPeriod",
"type": "uint256"
}
],
"name": "TWAPEpochChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousTWAPOracle",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newTWAPOracle",
"type": "address"
}
],
"name": "TWAPOracleChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "newTWAPSource",
"type": "address"
}
],
"name": "TWAPSourceAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "removedTWAPSource",
"type": "address"
}
],
"name": "TWAPSourceRemoved",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PERMIT_TYPEHASH",
"outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "account_", "type": "address" },
{ "internalType": "uint256", "name": "amount_", "type": "uint256" }
],
"name": "_burnFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newTWAPSourceDexPool_",
"type": "address"
}
],
"name": "addTWAPSource",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "spender", "type": "address" }
],
"name": "allowance",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "approve",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "account", "type": "address" }
],
"name": "balanceOf",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "account_", "type": "address" },
{ "internalType": "uint256", "name": "amount_", "type": "uint256" }
],
"name": "burnFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newTWAPEpochPeriod_",
"type": "uint256"
}
],
"name": "changeTWAPEpochPeriod",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "newTWAPOracle_", "type": "address" }
],
"name": "changeTWAPOracle",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "addedValue", "type": "uint256" }
],
"name": "increaseAllowance",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "account_", "type": "address" },
{ "internalType": "uint256", "name": "amount_", "type": "uint256" }
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner", "type": "address" }
],
"name": "nonces",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" },
{ "internalType": "uint256", "name": "deadline", "type": "uint256" },
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "twapSourceToRemove_",
"type": "address"
}
],
"name": "removeTWAPSource",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "vault_", "type": "address" }
],
"name": "setVault",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "recipient", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "transfer",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "sender", "type": "address" },
{ "internalType": "address", "name": "recipient", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "transferFrom",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "newOwner_", "type": "address" }
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "twapEpochPeriod",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "twapOracle",
"outputs": [
{ "internalType": "contract ITWAPOracle", "name": "", "type": "address" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "vault",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
}
]
================================================
FILE: abi/UniswapV3Pool2.json
================================================
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"name": "Burn",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"name": "Collect",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"name": "CollectProtocol",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "paid0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "paid1",
"type": "uint256"
}
],
"name": "Flash",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint16",
"name": "observationCardinalityNextOld",
"type": "uint16"
},
{
"indexed": false,
"internalType": "uint16",
"name": "observationCardinalityNextNew",
"type": "uint16"
}
],
"name": "IncreaseObservationCardinalityNext",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"indexed": false,
"internalType": "int24",
"name": "tick",
"type": "int24"
}
],
"name": "Initialize",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"indexed": false,
"internalType": "uint128",
"name": "amount",
"type": "uint128"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"name": "Mint",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol0Old",
"type": "uint8"
},
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol1Old",
"type": "uint8"
},
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol0New",
"type": "uint8"
},
{
"indexed": false,
"internalType": "uint8",
"name": "feeProtocol1New",
"type": "uint8"
}
],
"name": "SetFeeProtocol",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "int256",
"name": "amount0",
"type": "int256"
},
{
"indexed": false,
"internalType": "int256",
"name": "amount1",
"type": "int256"
},
{
"indexed": false,
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"indexed": false,
"internalType": "uint128",
"name": "liquidity",
"type": "uint128"
},
{
"indexed": false,
"internalType": "int24",
"name": "tick",
"type": "int24"
}
],
"name": "Swap",
"type": "event"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint128",
"name": "amount",
"type": "uint128"
}
],
"name": "burn",
"outputs": [
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint128",
"name": "amount0Requested",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1Requested",
"type": "uint128"
}
],
"name": "collect",
"outputs": [
{
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint128",
"name": "amount0Requested",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1Requested",
"type": "uint128"
}
],
"name": "collectProtocol",
"outputs": [
{
"internalType": "uint128",
"name": "amount0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "amount1",
"type": "uint128"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "fee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeGrowthGlobal0X128",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeGrowthGlobal1X128",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "flash",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "observationCardinalityNext",
"type": "uint16"
}
],
"name": "increaseObservationCardinalityNext",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "liquidity",
"outputs": [
{
"internalType": "uint128",
"name": "",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxLiquidityPerTick",
"outputs": [
{
"internalType": "uint128",
"name": "",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "uint128",
"name": "amount",
"type": "uint128"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "mint",
"outputs": [
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "observations",
"outputs": [
{
"internalType": "uint32",
"name": "blockTimestamp",
"type": "uint32"
},
{
"internalType": "int56",
"name": "tickCumulative",
"type": "int56"
},
{
"internalType": "uint160",
"name": "secondsPerLiquidityCumulativeX128",
"type": "uint160"
},
{
"internalType": "bool",
"name": "initialized",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32[]",
"name": "secondsAgos",
"type": "uint32[]"
}
],
"name": "observe",
"outputs": [
{
"internalType": "int56[]",
"name": "tickCumulatives",
"type": "int56[]"
},
{
"internalType": "uint160[]",
"name": "secondsPerLiquidityCumulativeX128s",
"type": "uint160[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "positions",
"outputs": [
{
"internalType": "uint128",
"name": "liquidity",
"type": "uint128"
},
{
"internalType": "uint256",
"name": "feeGrowthInside0LastX128",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "feeGrowthInside1LastX128",
"type": "uint256"
},
{
"internalType": "uint128",
"name": "tokensOwed0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "tokensOwed1",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "protocolFees",
"outputs": [
{
"internalType": "uint128",
"name": "token0",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "token1",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint8",
"name": "feeProtocol0",
"type": "uint8"
},
{
"internalType": "uint8",
"name": "feeProtocol1",
"type": "uint8"
}
],
"name": "setFeeProtocol",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tick",
"type": "int24"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"internalType": "uint128",
"name": "_liquidity",
"type": "uint128"
},
{
"internalType": "uint256",
"name": "_feeGrowthGlobal0X128",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_feeGrowthGlobal1X128",
"type": "uint256"
}
],
"name": "setState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int16",
"name": "wordPos",
"type": "int16"
},
{
"internalType": "uint256",
"name": "wordVal",
"type": "uint256"
}
],
"name": "setTickBitmap",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tick",
"type": "int24"
},
{
"internalType": "uint128",
"name": "liquidityGross",
"type": "uint128"
},
{
"internalType": "int128",
"name": "liquidityNet",
"type": "int128"
},
{
"internalType": "uint256",
"name": "feeGrowthOutside0X128",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "feeGrowthOutside1X128",
"type": "uint256"
},
{
"internalType": "bool",
"name": "initialized",
"type": "bool"
}
],
"name": "setTickInfo",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "slot0",
"outputs": [
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
},
{
"internalType": "int24",
"name": "tick",
"type": "int24"
},
{
"internalType": "uint16",
"name": "observationIndex",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "observationCardinality",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "observationCardinalityNext",
"type": "uint16"
},
{
"internalType": "uint8",
"name": "feeProtocol",
"type": "uint8"
},
{
"internalType": "bool",
"name": "unlocked",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
}
],
"name": "snapshotCumulativesInside",
"outputs": [
{
"internalType": "int56",
"name": "tickCumulativeInside",
"type": "int56"
},
{
"internalType": "uint160",
"name": "secondsPerLiquidityInsideX128",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "secondsInside",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "int256",
"name": "amountSpecified",
"type": "int256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "swap",
"outputs": [
{
"internalType": "int256",
"name": "amount0",
"type": "int256"
},
{
"internalType": "int256",
"name": "amount1",
"type": "int256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "int16",
"name": "",
"type": "int16"
}
],
"name": "tickBitmap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tickSpacing",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"name": "ticks",
"outputs": [
{
"internalType": "uint128",
"name": "liquidityGross",
"type": "uint128"
},
{
"internalType": "int128",
"name": "liquidityNet",
"type": "int128"
},
{
"internalType": "uint256",
"name": "feeGrowthOutside0X128",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "feeGrowthOutside1X128",
"type": "uint256"
},
{
"internalType": "int56",
"name": "tickCumulativeOutside",
"type": "int56"
},
{
"internalType": "uint160",
"name": "secondsPerLiquidityOutsideX128",
"type": "uint160"
},
{
"internalType": "uint32",
"name": "secondsOutside",
"type": "uint32"
},
{
"internalType": "bool",
"name": "initialized",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token0",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token1",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]
================================================
FILE: abi/Visor.json
================================================
[
{
"inputs": [
{ "internalType": "address", "name": "_pool", "type": "address" },
{ "internalType": "address", "name": "_owner", "type": "address" },
{ "internalType": "string", "name": "name", "type": "string" },
{ "internalType": "string", "name": "symbol", "type": "string" }
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "shares",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "int24",
"name": "tick",
"type": "int24"
},
{
"indexed": false,
"internalType": "uint256",
"name": "totalAmount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "totalAmount1",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "feeAmount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "feeAmount1",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "totalSupply",
"type": "uint256"
}
],
"name": "Rebalance",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "shares",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount1",
"type": "uint256"
}
],
"name": "Withdraw",
"type": "event"
},
{
"inputs": [],
"name": "PRECISION",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "spender", "type": "address" }
],
"name": "allowance",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address[]", "name": "listed", "type": "address[]" }
],
"name": "appendList",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "approve",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "account", "type": "address" }
],
"name": "balanceOf",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "baseLower",
"outputs": [{ "internalType": "int24", "name": "", "type": "int24" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "baseUpper",
"outputs": [{ "internalType": "int24", "name": "", "type": "int24" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "currentTick",
"outputs": [{ "internalType": "int24", "name": "tick", "type": "int24" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "uint256", "name": "deposit0", "type": "uint256" },
{ "internalType": "uint256", "name": "deposit1", "type": "uint256" },
{ "internalType": "address", "name": "to", "type": "address" }
],
"name": "deposit",
"outputs": [
{ "internalType": "uint256", "name": "shares", "type": "uint256" }
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "deposit0Max",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "deposit1Max",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "fee",
"outputs": [{ "internalType": "uint24", "name": "", "type": "uint24" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getBasePosition",
"outputs": [
{ "internalType": "uint128", "name": "liquidity", "type": "uint128" },
{ "internalType": "uint256", "name": "amount0", "type": "uint256" },
{ "internalType": "uint256", "name": "amount1", "type": "uint256" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLimitPosition",
"outputs": [
{ "internalType": "uint128", "name": "liquidity", "type": "uint128" },
{ "internalType": "uint256", "name": "amount0", "type": "uint256" },
{ "internalType": "uint256", "name": "amount1", "type": "uint256" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getTotalAmounts",
"outputs": [
{ "internalType": "uint256", "name": "total0", "type": "uint256" },
{ "internalType": "uint256", "name": "total1", "type": "uint256" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "addedValue", "type": "uint256" }
],
"name": "increaseAllowance",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "limitLower",
"outputs": [{ "internalType": "int24", "name": "", "type": "int24" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "limitUpper",
"outputs": [{ "internalType": "int24", "name": "", "type": "int24" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "", "type": "address" }],
"name": "list",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxTotalSupply",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pool",
"outputs": [
{
"internalType": "contract IUniswapV3Pool",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "int24", "name": "_baseLower", "type": "int24" },
{ "internalType": "int24", "name": "_baseUpper", "type": "int24" },
{ "internalType": "int24", "name": "_limitLower", "type": "int24" },
{ "internalType": "int24", "name": "_limitUpper", "type": "int24" },
{ "internalType": "address", "name": "feeRecipient", "type": "address" },
{ "internalType": "int256", "name": "swapQuantity", "type": "int256" }
],
"name": "rebalance",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "uint256", "name": "_deposit0Max", "type": "uint256" },
{ "internalType": "uint256", "name": "_deposit1Max", "type": "uint256" }
],
"name": "setDepositMax",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_maxTotalSupply",
"type": "uint256"
}
],
"name": "setMaxTotalSupply",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tickSpacing",
"outputs": [{ "internalType": "int24", "name": "", "type": "int24" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "toggleWhitelist",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "token0",
"outputs": [
{ "internalType": "contract IERC20", "name": "", "type": "address" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token1",
"outputs": [
{ "internalType": "contract IERC20", "name": "", "type": "address" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "recipient", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "transfer",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "sender", "type": "address" },
{ "internalType": "address", "name": "recipient", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "transferFrom",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "uint256", "name": "amount0", "type": "uint256" },
{ "internalType": "uint256", "name": "amount1", "type": "uint256" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"name": "uniswapV3MintCallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "int256", "name": "amount0Delta", "type": "int256" },
{ "internalType": "int256", "name": "amount1Delta", "type": "int256" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"name": "uniswapV3SwapCallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "whitelisted",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "uint256", "name": "shares", "type": "uint256" },
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "address", "name": "from", "type": "address" }
],
"name": "withdraw",
"outputs": [
{ "internalType": "uint256", "name": "amount0", "type": "uint256" },
{ "internalType": "uint256", "name": "amount1", "type": "uint256" }
],
"stateMutability": "nonpayable",
"type": "function"
}
]
================================================
FILE: docs/about-core-pool-config.md
================================================
### About Core Pool Config
A `PoolConfig` is a group of key meta parameters of an Uniswap V3 Core Pool. In details, `TickSpacing`, `Token0Address`, `Token1Address`, and `FeeAmount`.
If 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.
```typescript
let configurableCorePool: IConfigurableCorePool =
clientInstance.initCorePoolFromConfig(
new PoolConfig(60, "USDC", "ETH", FeeAmount.MEDIUM)
);
```
================================================
FILE: docs/building-a-client-instance.md
================================================
### Building a client instance
A `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.
We 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.
```typescript
// 1. Instantiate a SimulationDataManager
// this is for handling the internal data (snapshots, roadmaps, etc.)
let simulationDataManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance(
"Your file path to save the internal data"
);
let clientInstance: SimulatorClient = new SimulatorClient(
simulationDataManager
);
```
It's recommended to close the client when you finish with Tuner.
```typescript
await clientInstance.shutdown();
```
================================================
FILE: docs/configuration.md
================================================
### Configuration
When 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:
```javascript
module.exports = {
RPCProviderUrl: process.env.MAINNET_PROVIDER_URL,
};
```
================================================
FILE: docs/contributing.md
================================================
### Contributing
Please create an issue on github repo for any questions.
Thanks and enjoy!
================================================
FILE: docs/fetching-all-the-data-of-a-certain-pool-from-ethereum.md
================================================
### Fetching all the data of a certain pool from Ethereum
Tuner syncs the full state of the Pool from the event logs on chain, aiming to reproduce 100% as things happened in the mainnet.
For 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).
Specifically, 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.
Note: The database for event logs(external data) and the one to support functionality of Tuner(internal data) are different SQLite database files.
```typescript
export type EndBlockTypeWhenInit =
| number
| "latest"
| "afterDeployment"
| "afterInitialization";
export type EndBlockTypeWhenRecover =
| number
| "latestOnChain"
| "latestDownloaded"
| "afterDeployment"
| "afterInitialization";
```
For 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.
This 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.
`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.
An example of the whole process will be:
```typescript
import {
EndBlockTypeWhenRecover,
EventDataSourceType,
SimulationDataManager,
SimulatorClient,
SQLiteSimulationDataManager,
} from "@bella-defintech/uniswap-v3-simulator";
// 1. Instantiate a SimulationDataManager
// this is for handling the internal data (snapshots, roadmaps, etc.)
let simulationDataManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance(
"Your file path to save the internal data"
);
let clientInstance: SimulatorClient = new SimulatorClient(
simulationDataManager
);
// 2. Specify the core pool you want and time range of event logs to sync
let poolName = "events-test";
// This would be the contract address of a certain Uniswap V3 Pool
let poolAddress = "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8";
let endBlock: EndBlockTypeWhenRecover = 12374077;
// This is the rpc provider url for ethers.js, you can customize it here or use the value in tuner.config.js
let RPCProviderUrl: string | undefined = "Your customed RPCProviderUrl";
// 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
// Just a reminder, RPC endpoint is necessary for the simulator even if you choose to download events from Subgraph
let eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH;
// 3(a). This method helps you:
// Download event data of a certain Uniswap V3 pool from mainnet
// Pre-process the data to figure out the inputs of swap events
// Finally get the core pool instance
if (!exists(`${poolName}_${poolAddress}.db`)) {
let configurableCorePool: ConfigurableCorePool =
await clientInstance.initCorePoolFromMainnet(
poolName,
poolAddress,
endBlock,
RPCProviderUrl,
eventDataSourceType
);
}
// 3(b). This method helps you:
// Update and Pre-process event data of a certain Uniswap V3 pool from mainnet if necessary
// Build a simulated CorePool instance from the downloaded-and-pre-processed mainnet events
let configurableCorePool: ConfigurableCorePool =
await clientInstance.recoverFromMainnetEventDBFile(
`${poolName}_${poolAddress}.db`,
endBlock,
RPCProviderUrl,
eventDataSourceType
);
// 4. It's recommended to close the client when you finish with Tuner
await clientInstance.shutdown();
```
Note: 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.
================================================
FILE: docs/forking-and-retracing.md
================================================
### Forking & Retracing
During 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.
```typescript
// current state: foo
let poolStateId: string = configurableCorePool.getPoolState().id;
// some transactions...
// current state: bar
configurableCorePool.recover(poolStateId);
// current state: foo
```
Or just let the pool step back to last state.
```typescript
// current state: foo
let poolStateId: string = configurableCorePool.getPoolState().id;
// one transaction...
// current state: bar
configurableCorePool.stepBack();
// current state: foo
```
Sometime we want multiple instances to try out various of possibilities from current state, it's good time using `fork` to do that.
```typescript
let forkedConfigurableCorePool: ConfigurableCorePool =
configurableCorePool.fork();
// some transactions with configurableCorePool...
// some transactions with forkedConfigurableCorePool...
```
The forked pool will remain the same state as its parent and the two pools act independently.
Note: `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`.
================================================
FILE: docs/getting-a-core-pool-instance.md
================================================
### Getting a Core Pool instance
A simple way to get a Core Pool instance is to build a new one according to the `PoolConfig`.
```typescript
let configurableCorePool: IConfigurableCorePool =
clientInstance.initCorePoolFromConfig(
new PoolConfig(60, "USDC", "ETH", FeeAmount.MEDIUM)
);
```
And then don't forget to initialize that before executing any interaction.
```typescript
let sqrtPriceX96ForInitialization = JSBI.BigInt("4295128739");
await configurableCorePool.initialize(sqrtPriceX96ForInitialization);
```
================================================
FILE: docs/getting-a-pool-instance-with-the-data-fetched.md
================================================
### Getting a pool instance with the data fetched
Usually before work begins, the preparation includes the following steps:
1. Using mainnet data to initialize a core pool
2. Replaying events up to the specified block
`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).
If 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:
```typescript
// the database name containing downloaded-and-pre-processed mainnet events
let mainnetEventDBFilePath =
"events_0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8.db";
let simulationDataManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance(
"Your file path to save the internal data"
);
let clientInstance: SimulatorClient = new SimulatorClient(
simulationDataManager
);
// Specify an endBlock number
// the SimulatorClient will replay events up to that block
let endBlock = 12374077;
let configurableCorePool: ConfigurableCorePool =
await clientInstance.recoverFromMainnetEventDBFile(
mainnetEventDBFilePath,
endBlock
);
// Now you can do whatever you want with the simulated pool
// here we simply print out the square root price of the pool at the specified block height
console.log(configurableCorePool.getCorePool().sqrtPriceX96.toString());
```
================================================
FILE: docs/how-tuner-library-works.md
================================================
### How "Tuner" Library Works?
The 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:
**`SimulatorClient`** - _The high-level and easiest way to use the simulator_
  |\_\_ **`ConfigurableCorePool`** - _To give **`CorePool`** snapshot and roadmap capabilities_
  |\_\_ **`CorePool`** - _The re-implementation of_ [_Uniswap-V3-Core logic_](https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol)
  |\_\_ **`MainnetDataDownloader`** - _The download-and-update utility to retrieve mainnet events_
  |\_\_ **`EventDBManager`** - _To persist mainnet event data using SQLite_
  |\_\_ **`SimulatorRoadMapManager`** - _To take snapshots and do state-change roadmap tracking_
  |\_\_ **`SimulationDataManager`** - _To persist snapshots and roadmaps using SQLite_
There are 2 abstraction layers of the library:
#### Top-Level: [`SimulatorClient`](src/client/SimulatorClient.ts)
This 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.
#### Low-Level: 
If 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:
- **`ConfigurableCorePool`**
- **`MainnetDataDownloader`**
- **`SimulatorRoadMapManager`**
####
================================================
FILE: docs/installing-tuner.md
================================================
### Installing "Tuner"
{% embed url="https://www.npmjs.com/package/@bella-defintech/uniswap-v3-simulator" %}
```bash
yarn add @bella-defintech/uniswap-v3-simulator
```
```bash
yarn upgrade @bella-defintech/uniswap-v3-simulator
```
================================================
FILE: docs/interacting-with-core-pool.md
================================================
### Interacting with Core Pool
A `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`.
```typescript
let corePoolView: CorePoolView = configurableCorePool.getCorePool();
corePoolView.tickCurrent;
corePoolView.getTick(tickIndex);
corePoolView.getPosition(owner, tickLower, tickUpper);
```
Also, 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.
Based on the `CorePool`, a `ConfigurableCorePool` is a state machine providing utilities like post-processor, fork, snapshot, step back and recover.
```typescript
let amount0: JSBI, amount1: JSBI;
({ amount0, amount1 } = await configurableCorePool.mint(
testUser,
tickLower,
tickUpper,
liquidity
));
({ amount0, amount1 } = await configurableCorePool.burn(
testUser,
tickLower,
tickUpper,
liquidity
));
({ amount0, amount1 } = await configurableCorePool.swap(
zeroForOne,
amountSpecified,
sqrtPriceLimitX96
));
```
Since 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.
================================================
FILE: docs/loading-and-streaming-events-into-a-pool.md
================================================
### Loading and streaming events into a pool
Suppose a quant developer is going to backtest some strategy on mainnet pool.
This includes the following steps:
1. Using mainnet data to initialize a core pool
2. Replaying events up to a certain block as a checkpoint
3. Do some interaction as the strategy asks as interpolation to real transactions
4. Replaying events until the next checkpoint
5. Repeat step3-4 until events are run out
6. Evaluate performance of the strategy
Tuner 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).
When 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.
An example of streaming process will be:
```typescript
import {
ConfigurableCorePool,
EventDBManager,
EventType,
SimulationDataManager,
SimulatorClient,
SQLiteSimulationDataManager,
} from "@bella-defintech/uniswap-v3-simulator";
import { LiquidityEvent } from "@bella-defintech/uniswap-v3-simulator/dist/entity/LiquidityEvent";
import { SwapEvent } from "@bella-defintech/uniswap-v3-simulator/dist/entity/SwapEvent";
import JSBI from "jsbi";
// the database name containing downloaded-and-pre-processed mainnet events
let mainnetEventDBFilePath =
"events_0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8.db";
// build a client instance
let simulationDataManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance(
"Your file path to save the internal data"
);
let clientInstance: SimulatorClient = new SimulatorClient(
simulationDataManager
);
// Specify an endBlock number
// the SimulatorClient will replay events up to that block
let endBlock0 = 12374077;
// get a pool instance
let configurableCorePool: ConfigurableCorePool =
await clientInstance.recoverFromMainnetEventDBFile(
mainnetEventDBFilePath,
endBlock0
);
// get an EventDBManager instance to load events
let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);
// get and sort event by block number
let events: (LiquidityEvent | SwapEvent)[] = [];
let startBlock = 1000,
endBlock = 2000;
let mintEvents: LiquidityEvent[] =
await eventDB.getLiquidityEventsByBlockNumber(
EventType.MINT,
startBlock,
endBlock
);
let burnEvents: LiquidityEvent[] =
await eventDB.getLiquidityEventsByBlockNumber(
EventType.BURN,
startBlock,
endBlock
);
let swapEvents: SwapEvent[] = await eventDB.getSwapEventsByBlockNumber(
startBlock,
endBlock
);
events.push(...mintEvents);
events.push(...burnEvents);
events.push(...swapEvents);
events.sort(function (a, b) {
return a.blockNumber == b.blockNumber
? a.logIndex - b.logIndex
: a.blockNumber - b.blockNumber;
});
// replay events
for (let index = 0; index < events.length; index++) {
// avoid stack overflow for the possible recovering operation
if (index % 1000 == 0) {
configurableCorePool.takeSnapshot("");
}
let event = events[index];
switch (event.type) {
case EventType.MINT:
await configurableCorePool.mint(
event.recipient,
event.tickLower,
event.tickUpper,
event.liquidity
);
break;
case EventType.BURN:
await configurableCorePool.burn(
event.msgSender,
event.tickLower,
event.tickUpper,
event.liquidity
);
break;
case EventType.SWAP:
let zeroForOne: boolean = JSBI.greaterThan(event.amount0, JSBI.BigInt(0))
? true
: false;
await configurableCorePool.swap(
zeroForOne,
event.amountSpecified
//,event.sqrt_price_x96
);
break;
default:
// @ts-ignore: ExhaustiveCheck
const exhaustiveCheck: never = event;
}
}
```
Note: 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.
================================================
FILE: docs/performance.md
================================================
### Performance
Environment: AWS EC2 t3.xlarge on us-east-1, RPC provider by Alchemy, Subgraph by hosted service on the Graph
Downloading 260,000+ events of WETH-USDC-3000(upper-middle transaction volume within all Uniswap v3 core pools) until #14027607 (Jan 18, 2022 approximately):
- Tuner v0.1.3 **461.6m(>7 hour)**
- Tuner v0.1.4 **51.1m(<1 hour)**
Replaying over 124,000 mainnet events: **within 16 second**
================================================
FILE: docs/persisting-and-recovering.md
================================================
### Persisting & Recovering
If 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.
```typescript
configurableCorePool.takeSnapshot("description for snapshot");
```
Then don't forget to persist it so that you can recover/resume later.
```typescript
let snapshotId: string = await configurableCorePool.persistSnapshot();
```
Later you can recover the pool from any snapshot in the internal database(local database file with default SQLite implementation).
```typescript
let recoveredConfigurableCorePool: ConfigurableCorePool =
await clientInstance.recoverCorePoolFromSnapshot(snapshotId);
```
If you forget the snapshotId, you can list and check all snapshots by information like description or created timestamp.
```typescript
let snapshotProfiles: SnapshotProfile[] =
await clientInstance.listSnapshotProfiles();
```
================================================
FILE: docs/pool-state-and-transition.md
================================================
### PoolState & Transition
A `ConfigurableCorePool` is a state machine based on the math model aka `CorePool` of Uniswap v3 contract implementation.
Every 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`.
================================================
FILE: docs/post-processor.md
================================================
### Post-processor
A 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.
Take 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.
You can set a post-processor after every interaction in this way.
```typescript
configurableCorePool.updatePostProcessor(
(pool: ConfigurableCorePool, transition: TransitionView) => {
console.log(pool.getPoolState().id);
console.log(transition.getRecord().id);
return Promise.resolve();
}
);
```
Or set it with a specific interaction.
```typescript
await configurableCorePool.mint(
"0x01",
-887272,
887272,
JSBI.BigInt("10860507277202"),
(pool: ConfigurableCorePool, transition: TransitionView) => {
console.log(pool.getPoolState().id);
console.log(transition.getRecord().id);
return Promise.resolve();
}
);
```
Note: 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.
================================================
FILE: docs/quick-start.md
================================================
### Quick Start
```typescript
import {
SimulationDataManager,
SimulatorClient,
SQLiteSimulationDataManager,
} from "@bella-defintech/uniswap-v3-simulator";
// 1. Instantiate a SimulationDataManager
// this is for handling the internal data (snapshots, roadmaps, etc.)
let simulationDataManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance(
"Your file path to save the internal data"
);
let clientInstance: SimulatorClient = new SimulatorClient(
simulationDataManager
);
// 2. Initialize a pool simulation from mainnet
// specify the core pool you want and time range of event logs to sync
let poolName = "test";
// This would be the contract address of a certain Uniswap V3 Pool
let poolAddress = "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8";
// Specify an endBlock number
// the SimulatorClient will download data up to that block
// and save it to a sqlite database file locally
let endBlock = 12374077;
// 3. This method helps you:
// Download event data of a certain Uniswap V3 pool from mainnet
// Pre-process the data to figure out the inputs of swap events
await clientInstance.initCorePoolFromMainnet(
poolName,
poolAddress,
"afterDeployment"
);
// 4. Build a simulated CorePool instance from the downloaded-and-pre-processed mainnet events
let configurableCorePool = await clientInstance.recoverFromMainnetEventDBFile(
`${poolName}_${poolAddress}.db`,
endBlock
);
// 5. Now you can do whatever you want with the simulated pool
// here we simply print out the square root price of the pool at the specified block height
console.log(configurableCorePool.getCorePool().sqrtPriceX96.toString());
```
================================================
FILE: docs/simulator-roadmap-manager.md
================================================
### SimulatorRoadmapManager
If 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.
First let's get the instance of `SimulatorRoadmapManager` from `SimulatorClient`.
```typescript
let simulatorRoadmapManager: SimulatorRoadmapManager =
clientInstance.simulatorRoadmapManager;
```
You can list and check all `ConfigurableCorePool` created by Tuner within the program.
```typescript
let pools: ConfigurableCorePool[] = await simulatorRoadmapManager.listRoutes();
```
With a `ConfigurableCorePool` id, Tuner can print route from the first pool state to current pool state of the state machine in pretty format.
```typescript
await simulatorRoadmapManager.printRoute(configurableCorePool.id);
```
Also you can persist the route for selecting them in the internal database later.
```typescript
let roadmapId = await simulatorRoadmapManager.persistRoute(
configurableCorePool.id,
"description for roadmap"
);
```
Then load the roadmap and print the route in pretty format.
```typescript
await simulatorRoadmapManager.loadAndPrintRoute(roadmapId);
```
Note: 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.
================================================
FILE: examples/Uniswap-v3-Events-Downloader/EventsDownloader.ts
================================================
import { EndBlockTypeWhenInit, BSCDataDownloader } from "../../src";
import { EventDataSourceType } from "../../src/enum/EventDataSourceType";
import dotenv from "dotenv";
import * as log4js from "log4js";
log4js.configure({
appenders: {
out: { type: "stdout" },
app: { type: "fileSync", filename: "EventsDownloader.log" },
},
categories: { default: { appenders: ["out", "app"], level: "info" } },
});
const logger = log4js.getLogger("EventsDownloader");
// load .env file
dotenv.config();
async function main() {
// eth/usdt
let poolName = "eth";
let poolAddress = "0xBe141893E4c6AD9272e8C04BAB7E6a10604501a5";
let deploymentBlockNumber = 27254278;
// let endBlock: EndBlockTypeWhenInit = "latest";
// let endBlock: EndBlockTypeWhenInit = "afterInitialization";
// It will use RPCProviderUrl in tuner.config.js if this is undefined.
let RPCProviderUrl: string | undefined = undefined;
// 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.
// Just a reminder, RPC endpoint is necessary for the simulator even if you choose to download events from Subgraph.
let eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH;
let mainnetDataDownloader = new BSCDataDownloader(
RPCProviderUrl,
eventDataSourceType
);
await mainnetDataDownloader.download(
poolName,
poolAddress,
deploymentBlockNumber,
deploymentBlockNumber
);
}
main()
.then(() => process.exit(0))
.catch((error) => {
logger.error(error);
process.exit(1);
});
process.on("exit", () => {
log4js.shutdown(() => {
console.log("log has been flushed and closed");
});
});
================================================
FILE: examples/Uniswap-v3-Events-Downloader/EventsUpdater.ts
================================================
import {
EndBlockTypeWhenRecover,
BSCDataDownloader,
EventDBManager,
} from "../../src";
import { EventDataSourceType } from "../../src/enum/EventDataSourceType";
import dotenv from "dotenv";
import * as log4js from "log4js";
// load .env file
dotenv.config();
log4js.configure({
appenders: {
out: { type: "stdout", level: "info" },
app: { type: "fileSync", filename: "EventsUpdater.log", level: "info" },
},
categories: { default: { appenders: ["out", "app"], level: "info" } },
});
const logger = log4js.getLogger("EventsUpdater");
async function main() {
// let endBlock: EndBlockTypeWhenRecover = "latestDownloaded";
// It will use RPCProviderUrl in tuner.config.js if this is undefined.
let RPCProviderUrl: string | undefined = undefined;
// 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.
// Just a reminder, RPC endpoint is necessary for the simulator even if you choose to download events from Subgraph.
let eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH;
let mainnetEventDBFilePath =
"eth_0xBe141893E4c6AD9272e8C04BAB7E6a10604501a5.db";
let mainnetDataDownloader = new BSCDataDownloader(
RPCProviderUrl,
eventDataSourceType
);
let endBlock: EndBlockTypeWhenRecover = "latestOnChain";
await mainnetDataDownloader.update(mainnetEventDBFilePath, endBlock);
// await mainnetDataDownloader.replaceLiquidityEventsFromSubgraphToRPC(
// mainnetEventDBFilePath
// );
// Pre-process events only
// let { poolAddress } = mainnetDataDownloader.parseFromMainnetEventDBFilePath(
// mainnetEventDBFilePath
// );
// let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);
// await mainnetDataDownloader.preProcessEvents(poolAddress, eventDB);
}
main()
.then(() => process.exit(0))
.catch((error) => {
logger.error(error);
process.exit(1);
});
process.on("exit", () => {
log4js.shutdown(() => {
console.log("log has been flushed and closed");
});
});
================================================
FILE: examples/Uniswap-v3-Strategy-Backtest/README.md
================================================
# Bella-DeFinTech / uniswap-v3-bot
- We have made a demo as a platform to build and backtest strategy based on the Tuner.
- For reference: https://github.com/Bella-DeFinTech/uniswap-v3-bot/blob/main/test/Backtest.test.ts
================================================
FILE: hardhat.config.ts
================================================
import "@typechain/hardhat";
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";
export default {
defaultNetwork: "hardhat",
networks: {
mainnet: {
url: process.env.MAINNET_PROVIDER_URL,
},
hardhat: {
forking: {
url: process.env.FORKING_PROVIDER_URL,
blockNumber: 13578942,
},
mining: {
auto: true,
interval: 0,
},
allowUnlimitedContractSize: false,
},
},
paths: {
sources: "./test/contracts/src",
tests: "./test/contracts",
cache: "./test/cache",
artifacts: "./test/artifacts",
},
solidity: {
version: "0.7.6",
settings: {
optimizer: {
enabled: true,
runs: 1, // 800,
},
metadata: {
bytecodeHash: "none",
},
},
},
mocha: {
timeout: 3600000,
},
typechain: {
outDir: "test/typechain",
target: "ethers-v5",
alwaysGenerateOverloads: false,
},
};
================================================
FILE: package.json
================================================
{
"name": "@bella-defintech/uniswap-v3-simulator",
"version": "0.1.9",
"description": "the 'Tuner', a Uniswap V3 Simulator",
"keywords": [
"uniswap",
"uniswapv3",
"uniswap-v3",
"simulation",
"simulator",
"defi"
],
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"dist/**",
"abi/**",
"tuner.config.js"
],
"scripts": {
"generate-types": "typechain --target=ethers-v5 --out-dir src/typechain 'abi/*.json'",
"build": "tsc",
"test": "yarn mocha",
"lint": "yarn prettier --write .",
"clean": "hardhat clean",
"compile": "hardhat compile && yarn generate-types",
"script": "yarn ts-node",
"testcontracts": "hardhat test"
},
"license": "BUSL-1.1",
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@typechain/ethers-v5": "^7.2.0",
"@typechain/hardhat": "^2.3.1",
"@types/chai": "^4.2.21",
"@types/chai-as-promised": "^7.1.4",
"@types/mocha": "^9.0.0",
"@types/sinon": "^10.0.4",
"@types/sinon-chai": "^3.2.5",
"@types/sqlite3": "^3.1.7",
"@types/uuid": "^8.3.1",
"@uniswap/v3-core": "^1.0.0",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"ethereum-waffle": "^3.0.0",
"hardhat": "^2.6.0",
"mocha": "^9.0.3",
"mocha-chai-jest-snapshot": "^1.1.3",
"mochawesome": "^6.2.2",
"prettier": "2.3.2",
"sinon": "^11.1.2",
"sinon-chai": "^3.7.0",
"ts-node": "^10.2.0",
"typechain": "^5.2.0",
"typescript": "^4.3.5"
},
"dependencies": {
"bignumber.js": "^9.0.2",
"dayjs": "^1.10.6",
"dotenv": "^17.2.1",
"ethers": "^5.7.0",
"graphql": "^16.2.0",
"graphql-request": "^3.7.0",
"jsbi": "3.1.6",
"knex": "^0.95.10",
"log4js": "^6.9.1",
"reflect-metadata": "^0.1.13",
"sqlite3": "^5.0.2",
"typedjson": "^1.7.0",
"uuid": "^8.3.2"
},
"resolutions": {
"tar": "^4.4.18"
},
"mocha": {
"colors": true,
"spec": [
"test/*.{test,spec}.ts"
],
"reporter": [
"mochawesome"
],
"require": [
"ts-node/register"
],
"timeout": 360000000
}
}
================================================
FILE: src/client/BSCDataDownloader.ts
================================================
import { EventType } from "../enum/EventType";
import { EventDBManager } from "../manager/EventDBManager";
import { BigNumber, providers } from "ethers";
import { UniswapV3Pool2__factory as UniswapV3PoolFactory } from "../typechain/factories/UniswapV3Pool2__factory";
import { UniswapV3Pool2 as UniswapV3Pool } from "../typechain/UniswapV3Pool2";
import {
ConfigurableCorePool,
PoolConfig,
convertTokenStr,
exists,
getDatabaseNameFromPath,
} from "..";
import { LiquidityEvent } from "../entity/LiquidityEvent";
import { SwapEvent } from "../entity/SwapEvent";
import { SQLiteSimulationDataManager } from "../manager/SQLiteSimulationDataManager";
import { SimulationDataManager } from "../interface/SimulationDataManager";
import { printParams } from "../util/Serializer";
import JSBI from "jsbi";
import {
BSC_PANCAKE_V3_SUBGRAPH_ENDPOINT,
SUBGRAPH_API_KEY,
ZERO,
} from "../enum/InternalConstants";
import { EventDataSourceType } from "../enum/EventDataSourceType";
import { PoolState } from "../model/PoolState";
import { ConfigurableCorePool as ConfigurableCorePoolImpl } from "../core/ConfigurableCorePool";
import { SimulatorConsoleVisitor } from "../manager/SimulatorConsoleVisitor";
import { SimulatorPersistenceVisitor } from "../manager/SimulatorPersistenceVisitor";
import { SimulatorRoadmapManager } from "../manager/SimulatorRoadmapManager";
import {
EndBlockTypeWhenInit,
EndBlockTypeWhenRecover,
} from "../entity/EndBlockType";
import { loadConfig } from "../config/TunerConfig";
import { request, gql } from "graphql-request";
import { PositionManager } from "../manager/PositionManager";
import * as log4js from "log4js";
import { TypedEvent } from "../typechain/common";
const logger = log4js.getLogger("BSCDataDownloader");
const headers = {
Authorization: `Bearer ${SUBGRAPH_API_KEY}`,
};
export class BSCDataDownloader {
private RPCProvider: providers.JsonRpcProvider;
private eventDataSourceType: EventDataSourceType;
constructor(
RPCProviderUrl: string | undefined,
eventDataSourceType: EventDataSourceType
) {
if (RPCProviderUrl == undefined) {
let tunerConfig = loadConfig(undefined);
RPCProviderUrl = tunerConfig.RPCProviderUrl;
}
this.RPCProvider = new providers.JsonRpcProvider(RPCProviderUrl);
this.eventDataSourceType = eventDataSourceType;
}
async queryDeploymentBlockNumber(poolAddress: string): Promise<number> {
// TODO how to know accurate block number on contract deployment?
// Maybe use etherscan API or scan back mainnet trxs through the first event the contract emitted.
// BTW, for most cases, it's the same as Initialization event block number. Let's take this now.
return this.queryInitializationBlockNumber(poolAddress);
}
async queryInitializationBlockNumber(poolAddress: string): Promise<number> {
let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);
let initializeTopic = uniswapV3Pool.filters.Initialize();
let initializationEvent = await this.queryFilterWithRetry(
uniswapV3Pool,
initializeTopic
);
return initializationEvent[0].blockNumber;
}
async parseEndBlockTypeWhenInit(
toBlock: EndBlockTypeWhenInit,
poolAddress: string
): Promise<number> {
switch (toBlock) {
case "latest":
return (await this.RPCProvider.getBlock("latest")).number;
case "afterDeployment":
return await this.queryDeploymentBlockNumber(poolAddress);
case "afterInitialization":
return await this.queryInitializationBlockNumber(poolAddress);
default:
let latestOnChain = (await this.RPCProvider.getBlock("latest")).number;
return toBlock > latestOnChain ? latestOnChain : toBlock;
}
}
async parseEndBlockTypeWhenRecover(
latestDownloadedEventBlockNumber: number,
toBlock: EndBlockTypeWhenRecover,
poolAddress: string
): Promise<number> {
switch (toBlock) {
case "latestOnChain":
return (await this.RPCProvider.getBlock("latest")).number;
case "latestDownloaded":
return latestDownloadedEventBlockNumber;
case "afterDeployment":
return await this.queryDeploymentBlockNumber(poolAddress);
case "afterInitialization":
return await this.queryInitializationBlockNumber(poolAddress);
default:
let latestOnChain = (await this.RPCProvider.getBlock("latest")).number;
return toBlock > latestOnChain ? latestOnChain : toBlock;
}
}
generateMainnetEventDBFilePath(
poolName: string,
poolAddress: string
): string {
return `${poolName}_${poolAddress}.db`;
}
parseFromMainnetEventDBFilePath(filePath: string): {
poolName: string;
poolAddress: string;
} {
let databaseName = getDatabaseNameFromPath(filePath, ".db");
let nameArr = databaseName.split("_");
return { poolName: nameArr[0], poolAddress: nameArr[1] };
}
async download(
poolName: string = "",
poolAddress: string,
deploymentBlockNumber: number,
toBlock: EndBlockTypeWhenInit,
batchSize: number = 10000
) {
// check toBlock first
let toBlockAsNumber = await this.parseEndBlockTypeWhenInit(
toBlock,
poolAddress
);
let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);
if (toBlockAsNumber < deploymentBlockNumber)
throw new Error(
`The pool does not exist at block height: ${toBlockAsNumber}, it was deployed at block height: ${deploymentBlockNumber}`
);
let initializeTopic = uniswapV3Pool.filters.Initialize();
let initializationEvent = await this.queryFilterWithRetry(
uniswapV3Pool,
initializeTopic,
deploymentBlockNumber,
toBlockAsNumber
);
let initializationSqrtPriceX96 = initializationEvent[0].args.sqrtPriceX96;
let initializationEventBlockNumber = initializationEvent[0].blockNumber;
// check db file then
let filePath = this.generateMainnetEventDBFilePath(poolName, poolAddress);
if (exists(filePath))
throw new Error(
`The database file: ${filePath} already exists. You can either try to update or delete the database file.`
);
let eventDB = await EventDBManager.buildInstance(filePath);
try {
// query and record poolConfig
let poolConfig = new PoolConfig(
await uniswapV3Pool.tickSpacing(),
await uniswapV3Pool.token0(),
await uniswapV3Pool.token1(),
await uniswapV3Pool.fee()
);
await eventDB.addPoolConfig(poolConfig);
await eventDB.saveLatestEventBlockNumber(deploymentBlockNumber);
if (toBlock === "afterDeployment") return;
// record initialize event
await eventDB.addInitialSqrtPriceX96(
initializationSqrtPriceX96.toString()
);
await eventDB.saveInitializationEventBlockNumber(
initializationEventBlockNumber
);
await eventDB.saveLatestEventBlockNumber(initializationEventBlockNumber);
if (toBlock === "afterInitialization") return;
// download events after initialization
if (this.eventDataSourceType === EventDataSourceType.SUBGRAPH) {
await this.downloadEventsFromSubgraph(
uniswapV3Pool,
poolAddress.toLowerCase(),
poolConfig!.token0,
poolConfig!.token1,
eventDB,
initializationEventBlockNumber,
toBlockAsNumber,
batchSize
);
} else if (this.eventDataSourceType === EventDataSourceType.RPC) {
await this.downloadEventsFromRPC(
uniswapV3Pool,
eventDB,
initializationEventBlockNumber,
toBlockAsNumber,
batchSize
);
}
// await this.preProcessEvents(poolAddress, eventDB);
} finally {
await eventDB.close();
}
}
async replaceLiquidityEventsFromSubgraphToRPC(
mainnetEventDBFilePath: string,
batchSize: number = 10000
) {
let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);
let initializationEventBlockNumber =
await eventDB.getInitializationEventBlockNumber();
let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();
// remove incomplete events
await eventDB.deleteLiquidityEventsByBlockNumber(
EventType.MINT,
initializationEventBlockNumber,
latestEventBlockNumber
);
await eventDB.deleteLiquidityEventsByBlockNumber(
EventType.BURN,
initializationEventBlockNumber,
latestEventBlockNumber
);
// download events after initialization
let { poolAddress } = this.parseFromMainnetEventDBFilePath(
mainnetEventDBFilePath
);
let poolConfig = await eventDB.getPoolConfig();
let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);
await this.downloadEventsFromSubgraph(
uniswapV3Pool,
poolAddress.toLowerCase(),
poolConfig!.token0,
poolConfig!.token1,
eventDB,
initializationEventBlockNumber,
latestEventBlockNumber,
batchSize,
true
);
}
async update(
mainnetEventDBFilePath: string,
toBlock: EndBlockTypeWhenRecover,
batchSize: number = 10000
) {
// check dbfile first
let { poolAddress } = this.parseFromMainnetEventDBFilePath(
mainnetEventDBFilePath
);
if (!exists(mainnetEventDBFilePath))
throw new Error(
`The database file: ${mainnetEventDBFilePath} does not exist. Please download the data first.`
);
// check toBlock then
let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);
try {
let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();
// let deploymentBlockNumber = await this.queryDeploymentBlockNumber(
// poolAddress
// );
let toBlockAsNumber = await this.parseEndBlockTypeWhenRecover(
latestEventBlockNumber,
toBlock,
poolAddress
);
// if (toBlockAsNumber < deploymentBlockNumber)
// throw new Error("toBlock is too small, the pool hasn't been deployed.");
if (toBlockAsNumber < latestEventBlockNumber) {
logger.info("It's already up to date.");
return;
}
let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);
// check and record initialize event if needed
let updateInitializationEvent = false;
let initializationEventBlockNumber =
await eventDB.getInitializationEventBlockNumber();
if (0 == initializationEventBlockNumber) {
updateInitializationEvent = true;
let initializeTopic = uniswapV3Pool.filters.Initialize();
let initializationEvent = await this.queryFilterWithRetry(
uniswapV3Pool,
initializeTopic
);
await eventDB.addInitialSqrtPriceX96(
initializationEvent[0].args.sqrtPriceX96.toString()
);
initializationEventBlockNumber = initializationEvent[0].blockNumber;
await eventDB.saveInitializationEventBlockNumber(
initializationEventBlockNumber
);
await eventDB.saveLatestEventBlockNumber(
initializationEventBlockNumber
);
}
if (
!updateInitializationEvent &&
toBlockAsNumber == latestEventBlockNumber
) {
logger.info("It's already up to date.");
return;
}
let fromBlockAsNumber = updateInitializationEvent
? initializationEventBlockNumber
: latestEventBlockNumber + 1;
// remove incomplete events
await eventDB.deleteLiquidityEventsByBlockNumber(
EventType.MINT,
fromBlockAsNumber,
toBlockAsNumber
);
await eventDB.deleteLiquidityEventsByBlockNumber(
EventType.BURN,
fromBlockAsNumber,
toBlockAsNumber
);
await eventDB.deleteSwapEventsByBlockNumber(
fromBlockAsNumber,
toBlockAsNumber
);
// download events after initialization
let poolConfig = await eventDB.getPoolConfig();
if (this.eventDataSourceType === EventDataSourceType.SUBGRAPH) {
await this.downloadEventsFromSubgraph(
uniswapV3Pool,
poolAddress.toLowerCase(),
poolConfig!.token0,
poolConfig!.token1,
eventDB,
fromBlockAsNumber,
toBlockAsNumber,
batchSize
);
} else if (this.eventDataSourceType === EventDataSourceType.RPC) {
await this.downloadEventsFromRPC(
uniswapV3Pool,
eventDB,
fromBlockAsNumber,
toBlockAsNumber,
batchSize
);
}
// await this.preProcessEvents(poolAddress, eventDB);
} finally {
await eventDB.close();
}
}
async preProcessEvents(poolAddress: string, eventDB: EventDBManager) {
// let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();
// for verify burn events
// (deprecated)workaround for the bug of burn events, not working though
// await this.fixBurnEvents(poolAddress, eventDB, latestEventBlockNumber);
// for verify swap events
await this.preProcessSwapEvent(eventDB);
logger.info("Events have been pre-processed successfully.");
}
async fixBurnEvents(
poolAddress: string,
eventDB: EventDBManager,
endBlock: number
): Promise<void> {
let startBlock = await eventDB.getLatestVerifiedBurnBlockNumber();
if (startBlock == 0) {
let initializationEventBlockNumber =
await eventDB.getInitializationEventBlockNumber();
startBlock = initializationEventBlockNumber;
}
if (startBlock >= endBlock) {
logger.info("No burn events to fix.");
return;
}
let uniswapV3Pool = await this.getCorePoolContarct(poolAddress);
await this.fixBurnEventsFromRPC(
uniswapV3Pool,
eventDB,
startBlock,
endBlock,
10000
);
}
async initializeAndReplayEvents(
eventDB: EventDBManager,
configurableCorePool: ConfigurableCorePool,
endBlock: number,
onlyInitialize: boolean = false
): Promise<ConfigurableCorePool> {
let initializationEventBlockNumber =
await eventDB.getInitializationEventBlockNumber();
let initialSqrtPriceX96 = await eventDB.getInitialSqrtPriceX96();
await configurableCorePool.initialize(initialSqrtPriceX96);
if (onlyInitialize) return configurableCorePool;
// replay events to find swap input param we need
let startBlock = initializationEventBlockNumber;
let currBlock = startBlock;
let startTime = Date.now();
while (currBlock <= endBlock) {
let nextEndBlock =
this.nextBatch(currBlock) > endBlock
? endBlock
: this.nextBatch(currBlock);
let events = await this.getAndSortEventByBlock(
eventDB,
currBlock,
nextEndBlock
);
if (events.length > 0) {
await this.replayEventsAndAssertReturnValues(
eventDB,
configurableCorePool,
events
);
await eventDB.saveLatestVerifiedSwapBlockNumber(nextEndBlock);
let endTime = Date.now();
let duration = endTime - startTime;
logger.info(
`Replay events duration: ${duration}ms, events: ${
events.length
}, average duration per event: ${
duration / events.length
}ms, fromBlock: ${currBlock}, toBlock: ${nextEndBlock}`
);
startTime = endTime;
}
currBlock = nextEndBlock + 1;
}
return configurableCorePool;
}
private async downloadEventsFromSubgraph(
uniswapV3Pool: UniswapV3Pool,
poolAddress: string,
token0: string,
token1: string,
eventDB: EventDBManager,
fromBlock: number,
toBlock: number,
batchSize: number,
liquidityEventsOnly: boolean = false
) {
while (fromBlock <= toBlock) {
let endBlock =
fromBlock + batchSize > toBlock ? toBlock : fromBlock + batchSize;
let latestEventBlockNumber = Math.max(
fromBlock,
// the quality of burn events from RPC is far better than from subgraph, so we use RPC to get mint and burn events
await this.saveEventsFromRPC(
uniswapV3Pool,
eventDB,
EventType.MINT,
fromBlock,
endBlock
),
await this.saveEventsFromRPC(
uniswapV3Pool,
eventDB,
EventType.BURN,
fromBlock,
endBlock
),
liquidityEventsOnly
? 0
: await this.saveEventsFromSubgraph(
poolAddress,
token0,
token1,
eventDB,
EventType.SWAP,
fromBlock,
endBlock
)
);
logger.info(
`latestEventBlockNumber: ${latestEventBlockNumber}, fromBlock: ${fromBlock}, toBlock: ${toBlock}, batchSize: ${batchSize}`
);
if (!liquidityEventsOnly)
await eventDB.saveLatestEventBlockNumber(latestEventBlockNumber);
fromBlock += batchSize + 1;
}
logger.info(
"Events have been downloaded successfully. Please wait for pre-process to be done..."
);
}
private async fixBurnEventsFromRPC(
uniswapV3Pool: UniswapV3Pool,
eventDB: EventDBManager,
fromBlock: number,
toBlock: number,
batchSize: number
) {
while (fromBlock <= toBlock) {
let endBlock =
fromBlock + batchSize > toBlock ? toBlock : fromBlock + batchSize;
let events = await this.pullBurnEventsFromRPC(
uniswapV3Pool,
fromBlock,
endBlock
);
if (events.length > 0) {
for (let event of events) {
// logger.info(
// `Fix burn event: ${event.transactionHash}, logIndex: ${
// event.logIndex
// }, amount: ${event.args.amount.toString()}, tickLower: ${
// event.args.tickLower
// }, tickUpper: ${event.args.tickUpper}`
// );
let burnEvents = await eventDB.getBurnEventsByTransactionHash(
event.transactionHash
);
if (burnEvents.length == 0) {
logger.warn(
`Burn event: ${event.transactionHash}, logIndex: ${
event.logIndex
}, amount: ${event.args.amount.toString()}, tickLower: ${
event.args.tickLower
}, tickUpper: ${event.args.tickUpper} not found`
);
continue;
} else if (burnEvents.length > 1) {
logger.warn(
`Burn event: ${event.transactionHash}, logIndex: ${
event.logIndex
}, amount: ${event.args.amount.toString()}, tickLower: ${
event.args.tickLower
}, tickUpper: ${event.args.tickUpper} found multiple times: ${
burnEvents.length
}`
);
await eventDB.saveBurnEvent(
event.transactionHash,
event.logIndex,
event.args.amount.toString(),
event.args.amount0.toString(),
event.args.amount1.toString(),
event.args.tickLower,
event.args.tickUpper
);
}
}
await eventDB.saveLatestVerifiedBurnBlockNumber(
events[events.length - 1].blockNumber
);
}
fromBlock += batchSize + 1;
}
logger.info("Burn events have been fixed successfully.");
}
private async downloadEventsFromRPC(
uniswapV3Pool: UniswapV3Pool,
eventDB: EventDBManager,
fromBlock: number,
toBlock: number,
batchSize: number
) {
while (fromBlock <= toBlock) {
let endBlock =
fromBlock + batchSize > toBlock ? toBlock : fromBlock + batchSize;
let latestEventBlockNumber = Math.max(
await this.saveEventsFromRPC(
uniswapV3Pool,
eventDB,
EventType.MINT,
fromBlock,
endBlock
),
await this.saveEventsFromRPC(
uniswapV3Pool,
eventDB,
EventType.BURN,
fromBlock,
endBlock
),
await this.saveEventsFromRPC(
uniswapV3Pool,
eventDB,
EventType.SWAP,
fromBlock,
endBlock
)
);
await eventDB.saveLatestEventBlockNumber(latestEventBlockNumber);
fromBlock += batchSize + 1;
}
logger.info(
"Events have been downloaded successfully. Please wait for pre-process to be done..."
);
}
private async queryFilterWithRetry(
contract: any,
filter: any,
fromBlock?: number,
toBlock?: number
): Promise<any[]> {
const maxRetries = 5;
const baseDelay = 2000; // 2 seconds
const maxDelay = 30000; // 30 seconds
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
logger.info(
`RPC queryFilter attempt ${attempt}/${maxRetries} (${fromBlock}-${toBlock})`
);
const events = await contract.queryFilter(filter, fromBlock, toBlock);
logger.info(
`RPC queryFilter successful on attempt ${attempt}, found ${events.length} events`
);
return events;
} catch (error: any) {
logger.error(
`RPC queryFilter attempt ${attempt} failed:`,
error.message
);
// if it is the last attempt, throw an error
if (attempt === maxRetries) {
logger.error(
`All ${maxRetries} RPC queryFilter attempts failed. Last error:`,
error.message
);
throw new Error(
`RPC queryFilter failed after ${maxRetries} attempts: ${error.message}`
);
}
// calculate the delay time (exponential backoff)
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
logger.info(`Waiting ${delay}ms before retry...`);
// wait and retry
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
// this should not be reached, but for type safety
throw new Error("RPC queryFilter failed after all retries");
}
private async request(query: string) {
const maxRetries = 10;
const baseDelay = 1000; // 1 second
const maxDelay = 60000; // 1 minute
// proxy configuration - can be set through environment variables
const proxyConfig = {
host: process.env.HTTP_PROXY_HOST || process.env.HTTPS_PROXY_HOST,
port: parseInt(
process.env.HTTP_PROXY_PORT || process.env.HTTPS_PROXY_PORT || "0"
),
auth: process.env.HTTP_PROXY_AUTH || process.env.HTTPS_PROXY_AUTH,
};
// check if proxy is needed
const useProxy = proxyConfig.host && proxyConfig.port > 0;
// save original environment variables
const originalHttpProxy = process.env.HTTP_PROXY;
const originalHttpsProxy = process.env.HTTPS_PROXY;
const originalHttpProxyAuth = process.env.HTTP_PROXY_AUTH;
const originalHttpsProxyAuth = process.env.HTTPS_PROXY_AUTH;
try {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
logger.info(`GraphQL request attempt ${attempt}/${maxRetries}`);
// if proxy is configured, add proxy settings
if (useProxy) {
process.env.HTTP_PROXY = `http://${proxyConfig.host}:${proxyConfig.port}`;
process.env.HTTPS_PROXY = `http://${proxyConfig.host}:${proxyConfig.port}`;
if (proxyConfig.auth) {
process.env.HTTP_PROXY_AUTH = proxyConfig.auth;
process.env.HTTPS_PROXY_AUTH = proxyConfig.auth;
}
logger.info(`Using proxy: ${proxyConfig.host}:${proxyConfig.port}`);
}
const response = await request(
BSC_PANCAKE_V3_SUBGRAPH_ENDPOINT,
query,
{},
headers
);
// check if the response is valid
if (!response) {
throw new Error("Empty response received");
}
// check GraphQL errors
if (response.errors && response.errors.length > 0) {
const errorMessages = response.errors
.map((err: any) => err.message)
.join(", ");
throw new Error(`GraphQL errors: ${errorMessages}`);
}
logger.info(`GraphQL request successful on attempt ${attempt}`);
return response;
} catch (error: any) {
logger.error(`Request attempt ${attempt} failed:`, error.message);
// if it is the last attempt, throw an error
if (attempt === maxRetries) {
logger.error(
`All ${maxRetries} attempts failed. Last error:`,
error.message
);
throw new Error(
`GraphQL request failed after ${maxRetries} attempts: ${error.message}`
);
}
// calculate the delay time (exponential backoff)
const delay = Math.min(
baseDelay * Math.pow(2, attempt - 1),
maxDelay
);
logger.info(`Waiting ${delay}ms before retry...`);
// wait and retry
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
} finally {
// restore original environment variables
if (originalHttpProxy !== undefined) {
process.env.HTTP_PROXY = originalHttpProxy;
} else {
delete process.env.HTTP_PROXY;
}
if (originalHttpsProxy !== undefined) {
process.env.HTTPS_PROXY = originalHttpsProxy;
} else {
delete process.env.HTTPS_PROXY;
}
if (originalHttpProxyAuth !== undefined) {
process.env.HTTP_PROXY_AUTH = originalHttpProxyAuth;
} else {
delete process.env.HTTP_PROXY_AUTH;
}
if (originalHttpsProxyAuth !== undefined) {
process.env.HTTPS_PROXY_AUTH = originalHttpsProxyAuth;
} else {
delete process.env.HTTPS_PROXY_AUTH;
}
}
}
private async saveEventsFromSubgraph(
poolAddress: string,
token0: string,
token1: string,
eventDB: EventDBManager,
eventType: EventType,
fromBlock: number,
toBlock: number
): Promise<number> {
// let token0Decimals = tokens[0].decimals;
// let token1Decimals = tokens[1].decimals;
// let fromTimestamp = (await this.RPCProvider.getBlock(fromBlock)).timestamp;
// let toTimestamp = (await this.RPCProvider.getBlock(toBlock)).timestamp;
logger.info("--------------------------------");
logger.info(poolAddress, token0, token1, eventType);
logger.info(fromBlock, toBlock);
logger.info("--------------------------------");
let latestEventBlockNumber = fromBlock;
let skip = 0;
while (true) {
if (eventType === EventType.MINT) {
const query = gql`
query {
liquidityPool(id: "${poolAddress}") {
deposits(
first: 1000
skip: ${skip}
where: { blockNumber_gte: ${fromBlock}, blockNumber_lte: ${toBlock} }
orderBy: blockNumber
orderDirection: asc
) {
account {
id
}
blockNumber
hash
inputTokenAmounts
liquidity
logIndex
tickLower
tickUpper
timestamp
}
}
}
`;
logger.info("mint query: ", query);
let data = await this.request(query);
if (null == data.liquidityPool) {
latestEventBlockNumber = toBlock;
break;
}
let events = data.liquidityPool.deposits;
if (events.length > 0) {
const liquidityEvents = events.map((event) => {
let date = new Date(event.timestamp * 1000);
return {
type: eventType,
msg_sender: event.account.id,
recipient: "",
liquidity: convertTokenStr(event.liquidity),
amount0: convertTokenStr(event.inputTokenAmounts[0]),
amount1: convertTokenStr(event.inputTokenAmounts[1]),
tick_lower: event.tickLower,
tick_upper: event.tickUpper,
block_number: event.blockNumber,
transaction_hash: event.hash,
log_index: event.logIndex,
date: date,
};
});
await eventDB.batchInsertLiquidityEvents(liquidityEvents);
latestEventBlockNumber = events[events.length - 1].blockNumber;
}
if (events.length < 1000) {
break;
} else {
skip += 1000;
}
} else if (eventType === EventType.BURN) {
const query = gql`
query {
liquidityPool(id: "${poolAddress}") {
withdraws(
first: 1000
skip: ${skip}
where: { blockNumber_gte: ${fromBlock}, blockNumber_lte: ${toBlock} }
orderBy: blockNumber
orderDirection: asc
) {
account {
id
}
blockNumber
hash
inputTokenAmounts
liquidity
logIndex
tickLower
tickUpper
timestamp
}
}
}
`;
logger.info("burn query: ", query);
let data = await this.request(query);
if (null == data.liquidityPool) {
latestEventBlockNumber = toBlock;
break;
}
let events = data.liquidityPool.withdraws;
if (events.length > 0) {
const liquidityEvents = events.map((event) => {
let date = new Date(event.timestamp * 1000);
return {
type: eventType,
msg_sender: event.account.id,
recipient: "",
liquidity: convertTokenStr(event.liquidity),
amount0: convertTokenStr(event.inputTokenAmounts[0]),
amount1: convertTokenStr(event.inputTokenAmounts[1]),
tick_lower: event.tickLower,
tick_upper: event.tickUpper,
block_number: event.blockNumber,
transaction_hash: event.hash,
log_index: event.logIndex,
date: date,
};
});
await eventDB.batchInsertLiquidityEvents(liquidityEvents);
latestEventBlockNumber = events[events.length - 1].blockNumber;
}
if (events.length < 1000) {
break;
} else {
skip += 1000;
}
} else if (eventType === EventType.SWAP) {
const query = gql`
query {
liquidityPool(id: "${poolAddress}") {
swaps(
first: 1000
skip: ${skip}
where: { blockNumber_gte: ${fromBlock}, blockNumber_lte: ${toBlock} }
orderBy: blockNumber
orderDirection: asc
) {
amountIn
amountOut
blockNumber
hash
logIndex
nonce
tick
timestamp
account {
id
}
tokenIn {
id
}
tokenOut {
id
}
}
}
}
`;
logger.info("swap query: ", query);
let data = await this.request(query);
if (
null == data.liquidityPool ||
data.liquidityPool.swaps.length == 0
) {
latestEventBlockNumber = toBlock;
break;
}
let events = data.liquidityPool.swaps;
if (events.length > 0) {
const swapEvents = events.map((event) => {
let amount0;
let amount1;
if (token0.toLowerCase() === event.tokenIn.id.toLowerCase()) {
amount0 = event.amountIn;
amount1 = -event.amountOut;
} else {
amount0 = -event.amountOut;
amount1 = event.amountIn;
}
let date = new Date(event.timestamp * 1000);
return {
msg_sender: event.account.id,
recipient: event.account.id,
amount0: convertTokenStr(amount0),
amount1: convertTokenStr(amount1),
sqrt_price_x96: "-1",
liquidity: "-1",
tick: event.tick,
block_number: event.blockNumber,
transaction_hash: event.hash,
log_index: event.logIndex,
date: date,
};
});
await eventDB.batchInsertSwapEvents(swapEvents);
latestEventBlockNumber = events[events.length - 1].blockNumber;
}
if (events.length < 1000) {
break;
} else {
skip += 1000;
}
}
}
return latestEventBlockNumber;
}
private async pullBurnEventsFromRPC(
uniswapV3Pool: UniswapV3Pool,
fromBlock: number,
toBlock: number
): Promise<
TypedEvent<
[string, number, number, BigNumber, BigNumber, BigNumber] & {
owner: string;
tickLower: number;
tickUpper: number;
amount: BigNumber;
amount0: BigNumber;
amount1: BigNumber;
}
>[]
> {
let topic = uniswapV3Pool.filters.Burn();
let events = await this.queryFilterWithRetry(
uniswapV3Pool,
topic,
fromBlock,
toBlock
);
return events;
}
private async saveEventsFromRPC(
uniswapV3Pool: UniswapV3Pool,
eventDB: EventDBManager,
eventType: EventType,
fromBlock: number,
toBlock: number
): Promise<number> {
let latestEventBlockNumber = fromBlock;
if (eventType === EventType.MINT) {
let topic = uniswapV3Pool.filters.Mint();
let events = await this.queryFilterWithRetry(
uniswapV3Pool,
topic,
fromBlock,
toBlock
);
if (events.length > 0) {
const liquidityEvents = await Promise.all(
events.map(async (event) => {
// to get better efficiency, we use local timestamp(inserted time) instead of block timestamp
return {
type: eventType,
msg_sender: event.args.owner,
recipient: "",
liquidity: event.args.amount.toString(),
amount0: event.args.amount0.toString(),
amount1: event.args.amount1.toString(),
tick_lower: event.args.tickLower,
tick_upper: event.args.tickUpper,
block_number: event.blockNumber,
transaction_hash: event.transactionHash,
log_index: event.logIndex,
date: new Date(),
};
})
);
await eventDB.batchInsertLiquidityEvents(liquidityEvents);
latestEventBlockNumber = Math.max(...events.map((e) => e.blockNumber));
}
} else if (eventType === EventType.BURN) {
let topic = uniswapV3Pool.filters.Burn();
let events = await this.queryFilterWithRetry(
uniswapV3Pool,
topic,
fromBlock,
toBlock
);
if (events.length > 0) {
const liquidityEvents = await Promise.all(
events.map(async (event) => {
return {
type: eventType,
msg_sender: event.args.owner,
recipient: "",
liquidity: event.args.amount.toString(),
amount0: event.args.amount0.toString(),
amount1: event.args.amount1.toString(),
tick_lower: event.args.tickLower,
tick_upper: event.args.tickUpper,
block_number: event.blockNumber,
transaction_hash: event.transactionHash,
log_index: event.logIndex,
date: new Date(),
};
})
);
await eventDB.batchInsertLiquidityEvents(liquidityEvents);
latestEventBlockNumber = Math.max(...events.map((e) => e.blockNumber));
}
} else if (eventType === EventType.SWAP) {
let topic = uniswapV3Pool.filters.Swap();
let events = await this.queryFilterWithRetry(
uniswapV3Pool,
topic,
fromBlock,
toBlock
);
if (events.length > 0) {
const swapEvents = await Promise.all(
events.map(async (event) => {
return {
msg_sender: event.args.sender,
recipient: event.args.recipient,
amount0: event.args.amount0.toString(),
amount1: event.args.amount1.toString(),
sqrt_price_x96: event.args.sqrtPriceX96.toString(),
liquidity: event.args.liquidity.toString(),
tick: event.args.tick,
block_number: event.blockNumber,
transaction_hash: event.transactionHash,
log_index: event.logIndex,
date: new Date(),
};
})
);
await eventDB.batchInsertSwapEvents(swapEvents);
latestEventBlockNumber = Math.max(...events.map((e) => e.blockNumber));
}
}
return latestEventBlockNumber;
}
async preProcessSwapEvent(eventDB: EventDBManager) {
let latestVerifiedSwapBlockNumber =
await eventDB.getLatestVerifiedSwapBlockNumber();
if (latestVerifiedSwapBlockNumber == 0) {
latestVerifiedSwapBlockNumber =
await eventDB.getInitializationEventBlockNumber();
}
let latestEventBlockNumber = await eventDB.getLatestEventBlockNumber();
if (latestVerifiedSwapBlockNumber >= latestEventBlockNumber) {
logger.info("No swap events to pre-process.");
return;
}
// initialize configurableCorePool
let simulatorDBManager: SimulationDataManager =
await SQLiteSimulationDataManager.buildInstance();
let poolConfig = await eventDB.getPoolConfig();
let configurableCorePool: ConfigurableCorePool =
new ConfigurableCorePoolImpl(
new PoolState(poolConfig),
new SimulatorRoadmapManager(simulatorDBManager),
new SimulatorConsoleVisitor(),
new SimulatorPersistenceVisitor(simulatorDBManager)
);
await this.initializeAndReplayEvents(
eventDB,
configurableCorePool,
await eventDB.getLatestEventBlockNumber()
);
await simulatorDBManager.close();
logger.info("Events have been pre-processed successfully.");
}
private nextBatch(currBlock: number) {
// we take an hour as step length, consider block interval as 0.75s then 60 * 60 / 0.75 = 4800
return currBlock + 4800;
}
private async getCorePoolContarct(
poolAddress: string
): Promise<UniswapV3Pool> {
return UniswapV3PoolFactory.connect(poolAddress, this.RPCProvider);
}
private async getAndSortEventByBlock(
eventDB: EventDBManager,
startBlock: number,
endBlock: number
): Promise<(LiquidityEvent | SwapEvent)[]> {
let events: (LiquidityEvent | SwapEvent)[] = [];
let mintEvents: LiquidityEvent[] =
await eventDB.getLiquidityEventsByBlockNumber(
EventType.MINT,
startBlock,
endBlock
);
let burnEvents: LiquidityEvent[] =
await eventDB.getLiquidityEventsByBlockNumber(
EventType.BURN,
startBlock,
endBlock
);
let swapEvents: SwapEvent[] = await eventDB.getSwapEventsByBlockNumber(
startBlock,
endBlock
);
events.push(...mintEvents);
events.push(...burnEvents);
events.push(...swapEvents);
events.sort(function (a, b) {
return a.blockNumber == b.blockNumber
? a.logIndex - b.logIndex
: a.blockNumber - b.blockNumber;
});
return events;
}
private async replayEventsAndAssertReturnValues(
eventDB: EventDBManager,
configurableCorePool: ConfigurableCorePool,
paramArr: (LiquidityEvent | SwapEvent)[]
): Promise<void> {
for (let index = 0; index < paramArr.length; index++) {
// avoid stack overflow
if (index % 4000 == 0) {
configurableCorePool.takeSnapshot("");
}
let param = paramArr[index];
let amount0: JSBI, amount1: JSBI;
switch (param.type) {
case EventType.MINT:
({ amount0, amount1 } = await configurableCorePool.mint(
param.msgSender,
param.tickLower,
param.tickUpper,
param.liquidity
));
if (
JSBI.notEqual(amount0, param.amount0) ||
JSBI.notEqual(amount1, param.amount1)
)
logger.warn(
`Mint result is not correct. We need to endure this error. Result: amount0: ${amount0}, amount1: ${amount1}. Event: ${printParams(
param
)}.`
);
logger.debug(
`Mint success. Event txn hash: ${param.transactionHash}, log_index: ${param.logIndex}.`
);
break;
case EventType.BURN:
// let positionManager =
// configurableCorePool.getCorePool().positionManager;
// let positions = positionManager.getPositionsByOwner(param.msgSender);
// if (positions.size == 0) {
// logger.warn(
// `Burn failed. No position found. We need to endure this error. Event: ${printParams(
// param
// )}.`
// );
// continue;
// }
// for (let [key, position] of positions.entries()) {
// let { owner, tickLower, tickUpper } =
// PositionManager.extractFromKey(key);
// ({ amount0, amount1 } = await configurableCorePool.burn(
// owner,
// tickLower,
// tickUpper,
// position.liquidity
// ));
// }
if (param.tickLower == null || param.tickUpper == null) {
logger.warn(
`Burn failed. TickLower or TickUpper is null. We need to endure this error. Event: ${printParams(
param
)}.`
);
continue;
}
({ amount0, amount1 } = await configurableCorePool.burn(
param.msgSender,
param.tickLower,
param.tickUpper,
param.liquidity
));
if (
JSBI.notEqual(amount0, param.amount0) ||
JSBI.notEqual(amount1, param.amount1)
)
logger.warn(
`Burn result is not correct. We need to endure this error. Result: amount0: ${amount0}, amount1: ${amount1}. Event: ${printParams(
param
)}.`
);
logger.debug(
`Burn success. Event txn hash: ${param.transactionHash}, log_index: ${param.logIndex}.`
);
break;
case EventType.SWAP:
// try-error to find `amountSpecified` and `sqrtPriceLimitX96` to resolve to the same result as swap event records
try {
// logger.info(
// `try to resolve swap event, event txn hash: ${param.transactionHash}, log_index: ${param.logIndex}`
// );
// let { amountSpecified, sqrtPriceX96 } =
// await configurableCorePool.resolveInputFromSwapResultEvent(param);
// logger.info(
// `amountSpecified: ${amountSpecified}, sqrtPriceX96: ${sqrtPriceX96}`
// );
let zeroForOne: boolean = JSBI.greaterThan(param.amount0, ZERO)
? true
: false;
let amountSpecified = param.amount0;
await configurableCorePool.swap(
zeroForOne,
amountSpecified,
undefined
);
// add AmountSpecified column to swap event if we need to
// if (ZERO == param.amountSpecified) {
// await eventDB.addAmountSpecified(
// param.id,
// amountSpecified.toString()
// );
// }
} catch (error) {
return Promise.reject(`Swap failed. Event: ${printParams(param)}.`);
}
logger.debug(`Swap success. Event: ${printParams(param)}.`);
break;
default:
// @ts-ignore: ExhaustiveCheck
const exhaustiveCheck: never = param;
}
}
}
}
================================================
FILE: src/client/SimulatorClient.ts
================================================
import { ConfigurableCorePool } from "../core/ConfigurableCorePool";
import { PoolState } from "../model/PoolState";
import { PoolConfig } from "../model/PoolConfig";
import { SimulationDataManager } from "../interface/SimulationDataManager";
import { Snapshot } from "../entity/Snapshot";
import { SimulatorRoadmapManager } from "../manager/SimulatorRoadmapManager";
import { SnapshotProfile } from "../entity/SnapshotProfile";
import { SimulatorRoadmapManager as ISimulatorRoadmapManager } from "../interface/SimulatorRoadmapManager";
import { ConfigurableCorePool as IConfigurableCorePool } from "../interface/ConfigurableCorePool";
import { SimulatorConsoleVisitor } from "../manager/SimulatorConsoleVisitor";
import { SimulatorPersistenceVisitor } from "../manager/SimulatorPersistenceVisitor";
import { EventDBManager } from "../manager/EventDBManager";
import { BSCDataDownloader } from "./BSCDataDownloader";
import {
EndBlockTypeWhenInit,
EndBlockTypeWhenRecover,
} from "../entity/EndBlockType";
import { EventDataSourceType } from "../enum/EventDataSourceType";
import { Tick } from "../model/Tick";
import { PoolStateHelper } from "../util/PoolStateHelper";
export class SimulatorClient {
private simulatorDBManager: SimulationDataManager;
private readonly _simulatorRoadmapManager: SimulatorRoadmapManager;
public get simulatorRoadmapManager(): ISimulatorRoadmapManager {
return this._simulatorRoadmapManager;
}
constructor(simulatorDBManager: SimulationDataManager) {
this.simulatorDBManager = simulatorDBManager;
this._simulatorRoadmapManager = new SimulatorRoadmapManager(
simulatorDBManager
);
}
// async initCorePoolFromMainnet(
// poolName: string = "",
// poolAddress: string,
// endBlock: EndBlockTypeWhenInit,
// RPCProviderUrl: string | undefined = undefined,
// eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH
// ): Promise<IConfigurableCorePool> {
// let mainnetDataDownloader: BSCDataDownloader = new BSCDataDownloader(
// RPCProviderUrl,
// eventDataSourceType
// );
// await mainnetDataDownloader.download(
// poolName,
// poolAddress,
// deploymentBlockNumber,
// endBlock
// );
// let eventDBFilePath = mainnetDataDownloader.generateMainnetEventDBFilePath(
// poolName,
// poolAddress
// );
// let eventDB = await EventDBManager.buildInstance(eventDBFilePath);
// try {
// let poolConfig = await eventDB.getPoolConfig();
// let configurableCorePool: IConfigurableCorePool =
// this.initCorePoolFromConfig(poolConfig!);
// if (endBlock == "afterDeployment") return configurableCorePool;
// let endBlockInNumber =
// await mainnetDataDownloader.parseEndBlockTypeWhenInit(
// endBlock,
// poolAddress
// );
// await mainnetDataDownloader.initializeAndReplayEvents(
// eventDB,
// configurableCorePool,
// endBlockInNumber,
// endBlock == "afterInitialization"
// );
// return configurableCorePool;
// } finally {
// await eventDB.close();
// }
// }
async recoverFromMainnetEventDBFile(
mainnetEventDBFilePath: string,
endBlock: EndBlockTypeWhenRecover,
RPCProviderUrl: string | undefined = undefined,
eventDataSourceType: EventDataSourceType = EventDataSourceType.SUBGRAPH
): Promise<IConfigurableCorePool> {
let mainnetDataDownloader: BSCDataDownloader = new BSCDataDownloader(
RPCProviderUrl,
eventDataSourceType
);
await mainnetDataDownloader.update(mainnetEventDBFilePath, endBlock);
let { poolAddress } = mainnetDataDownloader.parseFromMainnetEventDBFilePath(
mainnetEventDBFilePath
);
let eventDB = await EventDBManager.buildInstance(mainnetEventDBFilePath);
try {
let poolConfig = await eventDB.getPoolConfig();
let configurableCorePool: IConfigurableCorePool =
this.initCorePoolFromConfig(poolConfig!);
if (endBlock == "afterDeployment") return configurableCorePool;
let endBlockInNumber =
await mainnetDataDownloader.parseEndBlockTypeWhenRecover(
await eventDB.getLatestEventBlockNumber(),
endBlock,
poolAddress
);
await mainnetDataDownloader.initializeAndReplayEvents(
eventDB,
configurableCorePool,
endBlockInNumber,
endBlock == "afterInitialization"
);
return configurableCorePool;
} finally {
await eventDB.close();
}
}
initCorePoolFromConfig(poolConfig: PoolConfig): IConfigurableCorePool {
return new ConfigurableCorePool(
new PoolState(poolConfig),
this._simulatorRoadmapManager,
new SimulatorConsoleVisitor(),
new SimulatorPersistenceVisitor(this.simulatorDBManager)
);
}
initCorePoolFromOnchainData(
poolConfig: PoolConfig,
sqrtPriceX96: bigint,
liquidity: bigint,
tickCurrent: number,
feeGrowthGlobal0X128: bigint,
feeGrowthGlobal1X128: bigint,
populatedTicks: Map<number, Tick>
): IConfigurableCorePool {
return new ConfigurableCorePool(
new PoolState(poolConfig),
this._simulatorRoadmapManager,
new SimulatorConsoleVisitor(),
new SimulatorPersistenceVisitor(this.simulatorDBManager),
PoolStateHelper.buildCorePoolByOnchainData(
poolConfig,
sqrtPriceX96,
liquidity,
tickCurrent,
feeGrowthGlobal0X128,
feeGrowthGlobal1X128,
populatedTicks
)
);
}
recoverCorePoolFromSnapshot(
snapshotId: string
): Promise<IConfigurableCorePool> {
return this.getSnapshot(snapshotId).then((snapshot: Snapshot | undefined) =>
!snapshot
? Promise.reject("This snapshot doesn't exist!")
: Promise.resolve(
new ConfigurableCorePool(
PoolState.from(snapshot),
this._simulatorRoadmapManager,
new SimulatorConsoleVisitor(),
new SimulatorPersistenceVisitor(this.simulatorDBManager)
)
)
);
}
listSnapshotProfiles(): Promise<SnapshotProfile[]> {
return this.simulatorDBManager.getSnapshotProfiles();
}
shutdown(): Promise<void> {
return this.simulatorDBManager.close();
}
private getSnapshot(snapshotId: string): Promise<Snapshot | undefined> {
return this.simulatorDBManager.getSnapshot(snapshotId);
}
}
================================================
FILE: src/client/index.ts
================================================
export * from "./SimulatorClient";
export * from "./BSCDataDownloader";
================================================
FILE: src/config/TunerConfig.ts
================================================
import path from "path";
import * as fs from "fs";
export interface TunerConfig {
RPCProviderUrl: string;
}
export function loadConfig(file?: string): TunerConfig {
let customJson = {};
const configFile = file || path.join(process.cwd(), "tuner.config.js");
try {
customJson = require(configFile);
} catch (e) {
if (fs.existsSync(configFile)) {
throw new Error(`Cannot read Tuner JSON: ${configFile}: ${e}`);
}
}
const defaultJson = require(path.join(
__dirname,
"..",
"..",
"tuner.config.js"
));
const merged = mergeDeep(defaultJson, customJson);
return {
...merged,
};
}
export function mergeDeep(target: any, source: any) {
const isObject = (obj: any) => obj && typeof obj === "object";
if (!isObject(target) || !isObject(source)) {
return source;
}
Object.keys(source).forEach((key) => {
const targetValue = target[key];
const sourceValue = source[key];
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
target[key] = sourceValue; // Always use source key, if given
} else if (isObject(targetValue) && isObject(sourceValue)) {
target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue);
} else {
target[key] = sourceValue;
}
});
return target;
}
================================================
FILE: src/core/ConfigurableCorePool.ts
================================================
import JSBI from "jsbi";
import { PoolState } from "../model/PoolState";
import { Record } from "../entity/Record";
import { CorePool } from "./CorePool";
import { Visitable } from "../interface/Visitable";
import { ActionType } from "../enum/ActionType";
import { PoolStateHelper } from "../util/PoolStateHelper";
import { IdGenerator } from "../util/IdGenerator";
import { Transition } from "../model/Transition";
import { SimulatorVisitor } from "../interface/SimulatorVisitor";
import { SimulatorConsoleVisitor } from "../manager/SimulatorConsoleVisitor";
import { SimulatorPersistenceVisitor } from "../manager/SimulatorPersistenceVisitor";
import {
MethodParams,
ReturnParams,
MintParams,
BurnParams,
SwapParams,
CollectParams,
GeneralReturnParams,
InitializeParams,
} from "../interface/ActionParams";
import { SimulatorRoadmapManager } from "../manager/SimulatorRoadmapManager";
import { ConfigurableCorePool as IConfigurableCorePool } from "../interface/ConfigurableCorePool";
import { CorePoolView } from "../interface/CorePoolView";
import { PoolStateView } from "../interface/PoolStateView";
import { Transition as TransitionView } from "../interface/Transition";
import { SwapEvent } from "../entity/SwapEvent";
import { RAPID_POSITION_OWNER, ZERO } from "../enum/InternalConstants";
import { FullMath } from "..";
export class ConfigurableCorePool implements IConfigurableCorePool, Visitable {
readonly id: string;
private simulatorConsoleVisitor: SimulatorConsoleVisitor;
private simulatorPersistenceVisitor: SimulatorPersistenceVisitor;
private _poolState: PoolState;
private simulatorRoadmapManager: SimulatorRoadmapManager;
private corePool: CorePool;
private postProcessorCallback: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void> = async function () {};
constructor(
poolState: PoolState,
simulatorRoadmapManager: SimulatorRoadmapManager,
simulatorConsoleVisitor: SimulatorConsoleVisitor,
simulatorPersistenceVisitor: SimulatorPersistenceVisitor,
corePool?: CorePool
) {
this.simulatorConsoleVisitor = simulatorConsoleVisitor;
this.simulatorPersistenceVisitor = simulatorPersistenceVisitor;
this.id = IdGenerator.guid();
if (corePool) {
this.corePool = corePool;
} else {
if (poolState.hasSnapshot()) {
this.corePool = PoolStateHelper.buildCorePoolBySnapshot(
poolState.snapshot!
);
} else if (poolState.hasBaseSnapshot()) {
this.corePool = PoolStateHelper.buildCorePoolBySnapshot(
poolState.baseSnapshot!
);
} else {
this.corePool = PoolStateHelper.buildCorePoolByPoolConfig(
poolState.poolConfig
);
}
}
this._poolState = poolState;
this.simulatorRoadmapManager = simulatorRoadmapManager;
this.simulatorRoadmapManager.addPoolState(this.poolState);
this.simulatorRoadmapManager.addRoute(this);
}
getPoolState(): PoolStateView {
return this.poolState;
}
getCorePool(): CorePoolView {
return this.corePool;
}
initialize(
sqrtPriceX96: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<void> {
let currentPoolStateId = this.poolState.id;
try {
let res = this.corePool.initialize(sqrtPriceX96);
return this.postProcess(
ActionType.INITIALIZE,
{ type: ActionType.INITIALIZE, sqrtPriceX96 } as InitializeParams,
{} as ReturnParams,
postProcessorCallback
).then(() => Promise.resolve(res));
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
}
setCoreGlobalState(sqrtPriceX96: JSBI, liquidity: JSBI, tickCurrent: number) {
this.corePool.setCoreGlobalState(sqrtPriceX96, liquidity, tickCurrent);
}
rapidMint(
tickLower: number,
tickUpper: number,
amount: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.rapidMint(tickLower, tickUpper, amount);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
// TODO: rapid mint shoule be different from mint
return this.postProcess(
ActionType.MINT,
{
type: ActionType.MINT,
recipient: RAPID_POSITION_OWNER,
tickLower,
tickUpper,
amount,
} as MintParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
phantomMint(
recipient: string,
tickLower: number,
tickUpper: number,
amount: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.phantomMint(recipient, tickLower, tickUpper, amount);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
// TODO: phantom mint shoule be different from mint
return this.postProcess(
ActionType.MINT,
{
type: ActionType.MINT,
recipient,
tickLower,
tickUpper,
amount,
} as MintParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
mint(
recipient: string,
tickLower: number,
tickUpper: number,
amount: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.mint(recipient, tickLower, tickUpper, amount);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
return this.postProcess(
ActionType.MINT,
{
type: ActionType.MINT,
recipient,
tickLower,
tickUpper,
amount,
} as MintParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
rapidBurn(
tickLower: number,
tickUpper: number,
amount: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.rapidBurn(tickLower, tickUpper, amount);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
// TODO: rapid burn shoule be different from burn
return this.postProcess(
ActionType.BURN,
{
type: ActionType.BURN,
owner: RAPID_POSITION_OWNER,
tickLower,
tickUpper,
amount,
} as BurnParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
phantomBurn(
owner: string,
tickLower: number,
tickUpper: number,
amount: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.phantomBurn(owner, tickLower, tickUpper, amount);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
// TODO: phantom burn shoule be different from burn
return this.postProcess(
ActionType.BURN,
{
type: ActionType.BURN,
owner,
tickLower,
tickUpper,
amount,
} as BurnParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
burn(
owner: string,
tickLower: number,
tickUpper: number,
amount: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.burn(owner, tickLower, tickUpper, amount);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
return this.postProcess(
ActionType.BURN,
{
type: ActionType.BURN,
owner,
tickLower,
tickUpper,
amount,
} as BurnParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
collect(
recipient: string,
tickLower: number,
tickUpper: number,
amount0Requested: JSBI,
amount1Requested: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.collect(
recipient,
tickLower,
tickUpper,
amount0Requested,
amount1Requested
);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
return this.postProcess(
ActionType.COLLECT,
{
type: ActionType.COLLECT,
recipient,
tickLower,
tickUpper,
amount0Requested,
amount1Requested,
} as CollectParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
swap(
zeroForOne: boolean,
amountSpecified: JSBI,
sqrtPriceLimitX96?: JSBI,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<{ amount0: JSBI; amount1: JSBI }> {
let currentPoolStateId = this.poolState.id;
let res: GeneralReturnParams;
try {
res = this.corePool.swap(zeroForOne, amountSpecified, sqrtPriceLimitX96);
} catch (error) {
this.recover(currentPoolStateId);
throw error;
}
return this.postProcess(
ActionType.SWAP,
{
type: ActionType.SWAP,
zeroForOne,
amountSpecified,
sqrtPriceLimitX96,
} as SwapParams,
res,
postProcessorCallback
)
.then(() => Promise.resolve(res))
.catch((err) => {
this.recover(currentPoolStateId);
return Promise.reject(err);
});
}
querySwap(
zeroForOne: boolean,
amountSpecified: JSBI,
sqrtPriceLimitX96?: JSBI
): Promise<{ amount0: JSBI; amount1: JSBI; sqrtPriceX96: JSBI }> {
return Promise.resolve(
this.corePool.querySwap(zeroForOne, amountSpecified, sqrtPriceLimitX96)
);
}
async resolveInputFromSwapResultEvent(
param: SwapEvent
): Promise<{ amountSpecified: JSBI; sqrtPriceX96?: JSBI }> {
let tryWithDryRun = (
solutionIndex: number,
param: SwapEvent,
amountSpecified: JSBI,
sqrtPriceLimitX96?: JSBI
) => {
let zeroForOne: boolean = JSBI.greaterThan(param.amount0, ZERO)
? true
: false;
return this.querySwap(
zeroForOne,
amountSpecified,
sqrtPriceLimitX96
).then(({ amount0, amount1, sqrtPriceX96 }) => {
// console.log(
// `solutionIndex: ${solutionIndex}, amount0: ${amount0}, amount1: ${amount1}, sqrtPriceX96: ${sqrtPriceX96}`
// );
return true;
// JSBI.equal(amount0, param.amount0) &&
// JSBI.equal(amount1, param.amount1)
// &&
// JSBI.equal(sqrtPriceX96, param.sqrtPriceX96)
});
};
let solution1 = {
amountSpecified: JSBI.equal(param.liquidity, ZERO)
? FullMath.incrTowardInfinity(param.amount0)
: param.amount0,
sqrtPriceLimitX96: undefined,
};
let solution2 = {
amountSpecified: JSBI.equal(param.liquidity, ZERO)
? FullMath.incrTowardInfinity(param.amount1)
: param.amount1,
sqrtPriceLimitX96: undefined,
};
let solution3 = {
amountSpecified: param.amount0,
sqrtPriceLimitX96: undefined,
};
let solution4 = {
amountSpecified: param.amount1,
sqrtPriceLimitX96: undefined,
};
let solutionList: {
amountSpecified: JSBI;
sqrtPriceLimitX96?: JSBI;
}[] = [];
solutionList.push(solution1);
// solutionList.push(solution2);
// solutionList.push(solution3);
// solutionList.push(solution4);
// if (JSBI.notEqual(param.sqrtPriceX96, this.getCorePool().sqrtPriceX96)) {
// if (JSBI.equal(param.liquidity, JSBI.BigInt(-1))) {
// let solution5 = {
// amountSpecified: param.amount0,
// sqrtPriceLimitX96: param.sqrtPriceX96,
// };
// let solution6 = {
// amountSpecified: param.amount1,
// sqrtPriceLimitX96: param.sqrtPriceX96,
// };
// solutionList.push(solution5);
// solutionList.push(solution6);
// }
// // solutionList.push(solution1);
// // solutionList.push(solution2);
// }
for (let i = 0; i < solutionList.length; i++) {
if (
await tryWithDryRun(
i + 1,
param,
solutionList[i].amountSpecified,
solutionList[i].sqrtPriceLimitX96
)
) {
return {
amountSpecified: solutionList[i].amountSpecified,
sqrtPriceX96: solutionList[i].sqrtPriceLimitX96,
};
}
}
throw new Error(
"Can't resolve to the same as event records. Please check event input."
);
}
// user custom PostProcessor will be called after pool state transition finishes
updatePostProcessor(
callback: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
) {
this.postProcessorCallback = callback;
}
takeSnapshot(description: string): boolean {
if (this.poolState.hasSnapshot()) return false;
this.poolState.takeSnapshot(
description,
this.corePool.token0Balance,
this.corePool.token1Balance,
this.corePool.sqrtPriceX96,
this.corePool.liquidity,
this.corePool.tickCurrent,
this.corePool.feeGrowthGlobal0X128,
this.corePool.feeGrowthGlobal1X128,
this.corePool.tickManager,
this.corePool.positionManager
);
return true;
}
fork(): IConfigurableCorePool {
this.takeSnapshot("Automated for forking");
return new ConfigurableCorePool(
this.poolState.fork(),
this.simulatorRoadmapManager,
this.simulatorConsoleVisitor,
this.simulatorPersistenceVisitor
);
}
persistSnapshot(): Promise<string> {
return this.traversePoolStateChain(
this.simulatorPersistenceVisitor,
this.poolState.id,
this.poolState.id
).then(() => Promise.resolve(this.poolState.id));
}
stepBack() {
let fromTransition = this.poolState.transitionSource;
if (!fromTransition) {
throw new Error("This is already initial poolState.");
}
this.recover(fromTransition.source.id);
}
recover(poolStateId: string) {
if (!this.simulatorRoadmapManager.hasPoolState(poolStateId)) {
throw new Error("Can't find poolState, id: " + poolStateId);
}
let poolState: PoolState =
this.simulatorRoadmapManager.getPoolState(poolStateId)!;
this._poolState = poolState;
this.corePool = poolState.recoverCorePool();
}
get poolState(): PoolState {
return this._poolState;
}
accept(visitor: SimulatorVisitor): Promise<string> {
return visitor.visitConfigurableCorePool(this);
}
// this will take snapshot during PoolStates to speed up
showStateTransitionRoute(
toPoolStateId?: string,
fromPoolStateId?: string
): Promise<void> {
let simulatorConsoleVisitor: SimulatorVisitor =
new SimulatorConsoleVisitor();
return this.traversePoolStateChain(
simulatorConsoleVisitor,
toPoolStateId ? toPoolStateId : this.poolState.id,
fromPoolStateId
);
}
persistSnapshots(
toPoolStateId: string,
fromPoolStateId?: string
): Promise<Array<number>> {
let snapshotIds: Array<number> = [];
let poolStateVisitCallback = (_: PoolState, returnValue: number) => {
if (returnValue > 0) snapshotIds.push(returnValue);
};
return this.traversePoolStateChain(
this.simulatorPersistenceVisitor,
toPoolStateId,
fromPoolStateId,
poolStateVisitCallback
).then(() => Promise.resolve(snapshotIds));
}
private traversePoolStateChain(
visitor: SimulatorVisitor,
toPoolStateId: string,
fromPoolStateId?: string,
poolStateVisitCallback?: (poolState: PoolState, returnValue: any) => void
): Promise<void> {
if (
fromPoolStateId &&
!this.simulatorRoadmapManager.hasPoolState(fromPoolStateId)
) {
throw new Error("Can't find poolState, id: " + fromPoolStateId);
}
if (!this.simulatorRoadmapManager.hasPoolState(toPoolStateId)) {
throw new Error("Can't find poolState, id: " + toPoolStateId);
}
let toPoolState: PoolState =
this.simulatorRoadmapManager.getPoolState(toPoolStateId)!;
return this.accept(visitor).then(() =>
this.handleSingleStepOnChain(
toPoolState,
visitor,
fromPoolStateId,
poolStateVisitCallback
)
);
}
private handleSingleStepOnChain(
poolState: PoolState,
simulatorVisitor: SimulatorVisitor,
fromPoolStateId?: string,
poolStateVisitCallback?: (poolState: PoolState, returnValue: any) => void
): Promise<void> {
if (
!poolState.transitionSource ||
poolState.transitionSource.record.actionType == ActionType.FORK ||
poolState.id == fromPoolStateId
) {
return poolState
.accept(simulatorVisitor, poolStateVisitCallback)
.then(() => Promise.resolve());
} else {
let fromTransition = poolState.transitionSource!;
return (
this.handleSingleStepOnChain(
fromTransition.source,
simulatorVisitor,
fromPoolStateId,
poolStateVisitCallback
)
.then(() => fromTransition.accept(simulatorVisitor))
.then(() =>
poolState.accept(simulatorVisitor, poolStateVisitCallback)
)
// this will clear auto caching of snapshot in last PoolState to save memory space
.then(() => fromTransition.source.clearSnapshot(true))
.then(() => Promise.resolve())
);
}
}
private buildRecord(
actionType: ActionType,
actionParams: MethodParams,
actionReturnValues: ReturnParams
): Record {
return {
id: IdGenerator.guid(),
actionType,
actionParams,
actionReturnValues,
timestamp: new Date(),
};
}
private getNextPoolState(fromTransition: Transition) {
let nextPoolState = new PoolState(
this.poolState.poolConfig,
undefined,
fromTransition
);
fromTransition.target = nextPoolState;
return nextPoolState;
}
private postProcess(
actionType: ActionType,
actionParams: MethodParams,
actionReturnValues: ReturnParams,
postProcessorCallback?: (
configurableCorePool: IConfigurableCorePool,
transition: TransitionView
) => Promise<void>
): Promise<void> {
let record: Record = this.buildRecord(
actionType,
actionParams,
actionReturnValues
);
let transition: Transition = this.poolState.addTransitionTarget(record);
let nextPoolState: PoolState = this.getNextPoolState(transition);
this.simulatorRoadmapManager.addPoolState(nextPoolState);
this._poolState = nextPoolState;
let postProcessor = postProcessorCallback
? postProcessorCallback
: this.postProcessorCallback;
return postProcessor(this, transition);
}
}
================================================
FILE: src/core/CorePool.ts
================================================
import JSBI from "jsbi";
import assert from "assert";
import { TickManager } from "../manager/TickManager";
import { PositionManager } from "../manager/PositionManager";
import { Position } from "../model/Position";
import { TickMath } from "../util/TickMath";
import { SqrtPriceMath } from "../util/SqrtPriceMath";
import { StepComputations } from "../entity/StepComputations";
import {
ZERO,
ONE,
NEGATIVE_ONE,
Q128,
PROTOCOL_FEE_DENOMINATOR,
PROTOCOL_FEE,
} from "../enum/InternalConstants";
import { SwapMath } from "../util/SwapMath";
import { LiquidityMath } from "../util/LiquidityMath";
import { FullMath } from "../util/FullMath";
import { FeeAmount } from "../enum/FeeAmount";
import { TickView } from "../interface/TickView";
import { PositionView } from "../interface/PositionView";
export class CorePool {
readonly token0: string;
readonly token1: string;
readonly fee: FeeAmount;
readonly tickSpacing: number;
readonly maxLiquidityPerTick: JSBI;
private _token0Balance: JSBI;
private _token1Balance: JSBI;
private _sqrtPriceX96: JSBI;
private _liquidity: JSBI;
private _tickCurrent: number;
private _feeGrowthGlobal0X128: JSBI;
private _feeGrowthGlobal1X128: JSBI;
private _tickManager: TickManager;
private _positionManager: PositionManager;
constructor(
token0: string,
token1: string,
fee: FeeAmount,
tickSpacing: number,
token0Balance: JSBI = JSBI.BigInt(0),
token1Balance: JSBI = JSBI.BigInt(0),
sqrtPriceX96: JSBI = JSBI.BigInt(0),
liquidity: JSBI = JSBI.BigInt(0),
tickCurrent: number = 0,
feeGrowthGlobal0X128: JSBI = JSBI.BigInt(0),
feeGrowthGlobal1X128: JSBI = JSBI.BigInt(0),
tickManager: TickManager = new TickManager(),
positionManager: PositionManager = new PositionManager()
) {
this.token0 = token0;
this.token1 = token1;
this.fee = fee;
this.tickSpacing = tickSpacing;
this.maxLiquidityPerTick =
TickMath.tickSpacingToMaxLiquidityPerTick(tickSpacing);
this._token0Balance = token0Balance;
this._token1Balance = token1Balance;
this._sqrtPriceX96 = sqrtPriceX96;
this._liquidity = liquidity;
this._tickCurrent = tickCurrent;
this._feeGrowthGlobal0X128 = feeGrowthGlobal0X128;
this._feeGrowthGlobal1X128 = feeGrowthGlobal1X128;
this._tickManager = tickManager;
this._positionManager = positionManager;
}
public get token0Balance(): JSBI {
return this._token0Balance;
}
public get token1Balance(): JSBI {
return this._token1Balance;
}
public get sqrtPriceX96(): JSBI {
return this._sqrtPriceX96;
}
public get liquidity(): JSBI {
return this._liquidity;
}
public get tickCurrent(): number {
return this._tickCurrent;
}
public get feeGrowthGlobal0X128(): JSBI {
return this._feeGrowthGlobal0X128;
}
public get feeGrowthGlobal1X128(): JSBI {
return this._feeGrowthGlobal1X128;
}
public get tickManager(): TickManager {
return this._tickManager;
}
public get positionManager(): PositionManager {
return this._positionManager;
}
initialize(sqrtPriceX96: JSBI) {
assert(JSBI.equal(this.sqrtPriceX96, ZERO), "Already initialized!");
this._tickCurrent = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
this._sqrtPriceX96 = sqrtPriceX96;
}
/**
* Set the core global state of the pool
* 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.
* @param sqrtPriceX96 - The new sqrt price of the pool
* @param liquidity - The new liquidity of the pool
* @param tickCurrent - The new current tick of the pool
*/
setCoreGlobalState(sqrtPriceX96: JSBI, liquidity: JSBI, tickCurrent: number) {
this._sqrtPriceX96 = sqrtPriceX96;
this._liquidity = liquidity;
this._tickCurrent = tickCurrent;
}
phantomMint(
recipient: string,
tickLower: number,
tickUpper: number,
amount: JSBI
): { amount0: JSBI; amount1: JSBI } {
assert(JSBI.greaterThan(amount, ZERO), "Mint amount should greater than 0");
let amount0 = ZERO;
let amount1 = ZERO;
let positionStep = this.phantomModifyPosition(
recipient,
tickLower,
tickUpper,
amount
);
amount0 = positionStep.amount0;
amount1 = positionStep.amount1;
return {
amount0,
amount1,
};
}
rapidMint(
tickLower: number,
tickUpper: number,
amount: JSBI
): { amount0: JSBI; amount1: JSBI } {
assert(JSBI.greaterThan(amount, ZERO), "Mint amount should greater than 0");
let amount0 = ZERO;
let amount1 = ZERO;
let positionStep = this.rapidModifyPosition(tickLower, tickUpper, amount);
amount0 = positionStep.amount0;
amount1 = positionStep.amount1;
return {
amount0,
amount1,
};
}
mint(
recipient: string,
tickLower: number,
tickUpper: number,
amount: JSBI
): { amount0: JSBI; amount1: JSBI } {
assert(JSBI.greaterThan(amount, ZERO), "Mint amount should greater than 0");
let amount0 = ZERO;
let amount1 = ZERO;
let positionStep = this.modifyPosition(
recipient,
tickLower,
tickUpper,
amount
);
amount0 = positionStep.amount0;
amount1 = positionStep.amount1;
return {
amount0,
amount1,
};
}
phantomBurn(
owner: string,
tickLower: number,
tickUpper: number,
amount: JSBI
): { amount0: JSBI; amount1: JSBI } {
let { position, amount0, amount1 } = this.phantomModifyPosition(
owner,
tickLower,
tickUpper,
JSBI.unaryMinus(amount)
);
amount0 = JSBI.unaryMinus(amount0);
amount1 = JSBI.unaryMinus(amount1);
if (JSBI.greaterThan(amount0, ZERO) || JSBI.greaterThan(amount1, ZERO)) {
let newTokensOwed0 = JSBI.add(position.tokensOwed0, amount0);
let newTokensOwed1 = JSBI.add(position.tokensOwed1, amount1);
position.updateBurn(newTokensOwed0, newTokensOwed1);
}
return {
amount0,
amount1,
};
}
rapidBurn(
tickLower: number,
tickUpper: number,
amount: JSBI
): { amount0: JSBI; amount1: JSBI } {
let { amount0, amount1 } = this.rapidModifyPosition(
tickLower,
tickUpper,
JSBI.unaryMinus(amount)
);
amount0 = JSBI.unaryMinus(amount0);
amount1 = JSBI.unaryMinus(amount1);
return {
amount0,
amount1,
};
}
burn(
owner: string,
tickLower: number,
tickUpper: number,
amount: JSBI
): { amount0: JSBI; amount1: JSBI } {
let { position, amount0, amount1 } = this.modifyPosition(
owner,
tickLower,
tickUpper,
JSBI.unaryMinus(amount)
);
amount0 = JSBI.unaryMinus(amount0);
amount1 = JSBI.unaryMinus(amount1);
if (JSBI.greaterThan(amount0, ZERO) || JSBI.greaterThan(amount1, ZERO)) {
let newTokensOwed0 = JSBI.add(position.tokensOwed0, amount0);
let newTokensOwed1 = JSBI.add(position.tokensOwed1, amount1);
position.updateBurn(newTokensOwed0, newTokensOwed1);
}
return {
amount0,
amount1,
};
}
collect(
recipient: string,
tickLower: number,
tickUpper: number,
amount0Requested: JSBI,
amount1Requested: JSBI
): { amount0: JSBI; amount1: JSBI } {
this.checkTicks(tickLower, tickUpper);
let { amount0, amount1 } = this.positionManager.collectPosition(
recipient,
tickLower,
tickUpper,
amount0Requested,
amount1Requested
);
return {
amount0,
amount1,
};
}
querySwap(
zeroForOne: boolean,
amountSpecified: JSBI,
sqrtPriceLimitX96?: JSBI
): { amount0: JSBI; amount1: JSBI; sqrtPriceX96: JSBI } {
return this.handleSwap(
zeroForOne,
amountSpecified,
sqrtPriceLimitX96,
true
);
}
swap(
zeroForOne: boolean,
amountSpecified: JSBI,
sqrtPriceLimitX96?: JSBI
): { amount0: JSBI; amount1: JSBI } {
return this.handleSwap(
zeroForOne,
amountSpecified,
sqrtPriceLimitX96,
false
);
}
private handleSwap(
zeroForOne: boolean,
amountSpecified: JSBI,
sqrtPriceLimitX96: JSBI | undefined,
isStatic: boolean
): { amount0: JSBI; amount1: JSBI; sqrtPriceX96: JSBI } {
if (!sqrtPriceLimitX96)
sqrtPriceLimitX96 = zeroForOne
? JSBI.add(TickMath.MIN_SQRT_RATIO, ONE)
: JSBI.subtract(TickMath.MAX_SQRT_RATIO, ONE);
if (zeroForOne) {
assert(
JSBI.greaterThan(sqrtPriceLimitX96, TickMath.MIN_SQRT_RATIO),
"RATIO_MIN"
);
assert(
JSBI.lessThan(sqrtPriceLimitX96, this.sqrtPriceX96),
"RATIO_CURRENT"
);
} else {
assert(
JSBI.lessThan(sqrtPriceLimitX96, TickMath.MAX_SQRT_RATIO),
"RATIO_MAX"
);
assert(
JSBI.greaterThan(sqrtPriceLimitX96, this.sqrtPriceX96),
"RATIO_CURRENT"
);
}
const exactInput = JSBI.greaterThanOrEqual(amountSpecified, ZERO);
// keep track of swap state
const state = {
amountSpecifiedRemaining: amountSpecified,
amountCalculated: ZERO,
sqrtPriceX96: this.sqrtPriceX96,
tick: this.tickCurrent,
liquidity: this.liquidity,
feeGrowthGlobalX128: zeroForOne
? this._feeGrowthGlobal0X128
: this._feeGrowthGlobal1X128,
};
// start swap while loop
while (
JSBI.notEqual(state.amountSpecifiedRemaining, ZERO) &&
JSBI.notEqual(state.sqrtPriceX96, sqrtPriceLimitX96)
) {
let step: StepComputations = {
sqrtPriceStartX96: ZERO,
tickNext: 0,
initialized: false,
sqrtPriceNextX96: ZERO,
amountIn: ZERO,
amountOut: ZERO,
feeAmount: ZERO,
};
step.sqrtPriceStartX96 = state.sqrtPriceX96;
// because each iteration of the while loop rounds, we can't optimize this code (relative to the smart contract)
// by simply traversing to the next available tick, we instead need to exactly replicate
// tickBitmap.nextInitializedTickWithinOneWord
({ nextTick: step.tickNext, initialized: step.initialized } =
this.tickManager.getNextInitializedTick(
state.tick,
this.tickSpacing,
zeroForOne
));
if (step.tickNext < TickMath.MIN_TICK) {
step.tickNext = TickMath.MIN_TICK;
} else if (step.tickNext > TickMath.MAX_TICK) {
step.tickNext = TickMath.MAX_TICK;
}
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
[state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount] =
SwapMath.computeSwapStep(
state.sqrtPriceX96,
(
zeroForOne
? JSBI.lessThan(step.sqrtPriceNextX96, sqrtPriceLimitX96)
: JSBI.greaterThan(step.sqrtPriceNextX96, sqrtPriceLimitX96)
)
? sqrtPriceLimitX96
: step.sqrtPriceNextX96,
state.liquidity,
state.amountSpecifiedRemaining,
this.fee
);
if (exactInput) {
state.amountSpecifiedRemaining = JSBI.subtract(
state.amountSpecifiedRemaining,
JSBI.add(step.amountIn, step.feeAmount)
);
state.amountCalculated = JSBI.subtract(
state.amountCalculated,
step.amountOut
);
} else {
state.amountSpecifiedRemaining = JSBI.add(
state.amountSpecifiedRemaining,
step.amountOut
);
state.amountCalculated = JSBI.add(
state.amountCalculated,
JSBI.add(step.amountIn, step.feeAmount)
);
}
// // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee
// if (cache.feeProtocol > 0) {
// uint256 delta = (step.feeAmount.mul(cache.feeProtocol)) / PROTOCOL_FEE_DENOMINATOR;
// step.feeAmount -= delta;
// state.protocolFee += uint128(delta);
// }
// if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee
let delta = JSBI.divide(
JSBI.multiply(step.feeAmount, PROTOCOL_FEE),
PROTOCOL_FEE_DENOMINATOR
);
step.feeAmount = JSBI.subtract(step.feeAmount, delta);
if (JSBI.greaterThan(state.liquidity, ZERO))
state.feeGrowthGlobalX128 = JSBI.add(
state.feeGrowthGlobalX128,
FullMath.mulDiv(step.feeAmount, Q128, state.liquidity)
);
if (JSBI.equal(state.sqrtPriceX96, step.sqrtPriceNextX96)) {
// if the tick is initialized, run the tick transition
if (step.initialized) {
let nextTick = this.tickManager.getTickAndInitIfAbsent(step.tickNext);
let liquidityNet = isStatic
? nextTick.liquidityNet
: nextTick.cross(
zeroForOne
? state.feeGrowthGlobalX128
: this._feeGrowthGlobal0X128,
zeroForOne
? this._feeGrowthGlobal1X128
: state.feeGrowthGlobalX128
);
// if we're moving leftward, we interpret liquidityNet as the opposite sign
// safe because liquidityNet cannot be type(int128).min
if (zeroForOne)
liquidityNet = JSBI.multiply(liquidityNet, NEGATIVE_ONE);
state.liquidity = LiquidityMath.addDelta(
state.liquidity,
liquidityNet
);
}
state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;
} else if (JSBI.notEqual(state.sqrtPriceX96, step.sqrtPriceStartX96)) {
// recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved
state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
}
}
if (!isStatic) {
this._sqrtPriceX96 = state.sqrtPriceX96;
if (state.tick != this.tickCurrent) this._tickCurrent = state.tick;
if (JSBI.notEqual(state.liquidity, this._liquidity))
this._liquidity = state.liquidity;
// update fee growth global
if (zeroForOne) {
this._feeGrowthGlobal0X128 = state.feeGrowthGlobalX128;
} else {
this._feeGrowthGlobal1X128 = state.feeGrowthGlobalX128;
}
}
let [amount0, amount1] =
zeroForOne == exactInput
? [
JSBI.subtract(amountSpecified, state.amountSpecifiedRemaining),
state.amountCalculated,
]
: [
state.amountCalculated,
JSBI.subtract(amountSpecified, state.amountSpecifiedRemaining),
];
return { amount0, amount1, sqrtPriceX96: state.sqrtPriceX96 };
}
private checkTicks(tickLower: number, tickUpper: number) {
assert(tickLower < tickUpper, "tickLower should lower than tickUpper");
assert(
tickLower >= TickMath.MIN_TICK,
"tickLower should NOT lower than MIN_TICK"
);
assert(
tickUpper <= TickMath.MAX_TICK,
"tickUpper should NOT greater than MAX_TICK"
);
}
private phantomModifyPosition(
owner: string,
tickLower: number,
tickUpper: number,
liquidityDelta: JSBI
): { position: Position; amount0: JSBI; amount1: JSBI } {
this.checkTicks(tickLower, tickUpper);
let amount0: JSBI = ZERO,
amount1: JSBI = ZERO;
let positionView: PositionView = this.getPosition(
owner,
tickLower,
tickUpper
);
if (JSBI.lessThan(liquidityDelta, ZERO)) {
const negatedLiquidityDelta = JSBI.multiply(liquidityDelta, NEGATIVE_ONE);
assert(
JSBI.greaterThanOrEqual(positionView.liquidity, negatedLiquidityDelta),
"Liquidity Underflow"
);
}
// check ticks pass, update position
let position = this.phantomUpdatePosition(
owner,
tickLower,
tickUpper,
liquidityDelta
);
// use switch or pattern matching
// check if liquidity happen add() or remove()
if (JSBI.notEqual(liquidityDelta, ZERO)) {
if (this.tickCurrent < tickLower) {
amount0 = SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
} else if (this.tickCurrent < tickUpper) {
amount0 = SqrtPriceMath.getAmount0Delta(
this._sqrtPriceX96,
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
this._sqrtPriceX96,
liquidityDelta
);
} else {
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
}
}
return {
position,
amount0,
amount1,
};
}
private rapidModifyPosition(
tickLower: number,
tickUpper: number,
liquidityDelta: JSBI
): { amount0: JSBI; amount1: JSBI } {
this.checkTicks(tickLower, tickUpper);
let amount0: JSBI = ZERO,
amount1: JSBI = ZERO;
// check ticks pass, update position
this.rapidUpdatePosition(tickLower, tickUpper, liquidityDelta);
// use switch or pattern matching
// check if liquidity happen add() or remove()
if (JSBI.notEqual(liquidityDelta, ZERO)) {
if (this.tickCurrent < tickLower) {
amount0 = SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
} else if (this.tickCurrent < tickUpper) {
amount0 = SqrtPriceMath.getAmount0Delta(
this._sqrtPriceX96,
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
this._sqrtPriceX96,
liquidityDelta
);
this._liquidity = LiquidityMath.addDelta(
this._liquidity,
liquidityDelta
);
} else {
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
}
}
return {
amount0,
amount1,
};
}
private modifyPosition(
owner: string,
tickLower: number,
tickUpper: number,
liquidityDelta: JSBI
): { position: Position; amount0: JSBI; amount1: JSBI } {
this.checkTicks(tickLower, tickUpper);
let amount0: JSBI = ZERO,
amount1: JSBI = ZERO;
let positionView: PositionView = this.getPosition(
owner,
tickLower,
tickUpper
);
if (JSBI.lessThan(liquidityDelta, ZERO)) {
const negatedLiquidityDelta = JSBI.multiply(liquidityDelta, NEGATIVE_ONE);
assert(
JSBI.greaterThanOrEqual(positionView.liquidity, negatedLiquidityDelta),
"Liquidity Underflow"
);
}
// check ticks pass, update position
let position = this.updatePosition(
owner,
tickLower,
tickUpper,
liquidityDelta
);
// use switch or pattern matching
// check if liquidity happen add() or remove()
if (JSBI.notEqual(liquidityDelta, ZERO)) {
if (this.tickCurrent < tickLower) {
amount0 = SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
} else if (this.tickCurrent < tickUpper) {
amount0 = SqrtPriceMath.getAmount0Delta(
this._sqrtPriceX96,
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
this._sqrtPriceX96,
liquidityDelta
);
this._liquidity = LiquidityMath.addDelta(
this._liquidity,
liquidityDelta
);
} else {
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
liquidityDelta
);
}
}
return {
position,
amount0,
amount1,
};
}
private phantomUpdatePosition(
owner: string,
tickLower: number,
tickUpper: number,
liquidityDelta: JSBI
): Position {
let position: Position = this.positionManager.getPositionAndInitIfAbsent(
owner,
tickLower,
tickUpper
);
let feeGrowthInsideStep = this.tickManager.getFeeGrowthInside(
tickLower,
tickUpper,
this.tickCurrent,
this.feeGrowthGlobal0X128,
this.feeGrowthGlobal1X128
);
position.update(
liquidityDelta,
feeGrowthInsideStep.feeGrowthInside0X128,
feeGrowthInsideStep.feeGrowthInside1X128
);
return position;
}
private rapidUpdatePosition(
tickLower: number,
tickUpper: number,
liquidityDelta: JSBI
) {
let flippedLower: boolean = false;
let flippedUpper: boolean = false;
if (JSBI.notEqual(liquidityDelta, ZERO)) {
flippedLower = this.tickManager
.getTickAndInitIfAbsent(tickLower)
.update(
liquidityDelta,
this.tickCurrent,
this.feeGrowthGlobal0X128,
this.feeGrowthGlobal1X128,
false,
this.maxLiquidityPerTick
);
flippedUpper = this.tickManager
.getTickAndInitIfAbsent(tickUpper)
.update(
liquidityDelta,
this.tickCurrent,
this.feeGrowthGlobal0X128,
this.feeGrowthGlobal1X128,
true,
this.maxLiquidityPerTick
);
}
if (JSBI.lessThan(liquidityDelta, ZERO)) {
if (flippedLower) {
this.tickManager.clear(tickLower);
}
if (flippedUpper) {
this.tickManager.clear(tickUpper);
}
}
}
private updatePosition(
owner: string,
tickLower: number,
tickUpper: number,
liquidityDelta: JSBI
): Position {
let position: Position = this.positionManager.getPositionAndInitIfAbsent(
owner,
tickLower,
tickUpper
);
let flippedLower: boolean = false;
let flippedUpper: boolean = false;
if (JSBI.notEqual(liquidityDelta, ZERO)) {
flippedLower = this.tickManager
.getTickAndInitIfAbsent(tickLower)
.update(
liquidityDelta,
this.tickCurrent,
this.feeGrowthGlobal0X128,
this.feeGrowthGlobal1X128,
false,
this.maxLiquidityPerTick
);
flippedUpper = this.tickManager
.getTickAndInitIfAbsent(tickUpper)
.update(
liquidityDelta,
this.tickCurrent,
this.feeGrowthGlobal0X128,
this.feeGrowthGlobal1X128,
true,
this.maxLiquidityPerTick
);
}
let feeGrowthInsideStep = this.tickManager.getFeeGrowthInside(
tickLower,
tickUpper,
this.tickCurrent,
this.feeGrowthGlobal0X128,
this.feeGrowthGlobal1X128
);
position.update(
liquidityDelta,
feeGrowthInsideStep.feeGrowthInside0X128,
feeGrowthInsideStep.feeGrowthInside1X128
);
if (JSBI.lessThan(liquidityDelta, ZERO)) {
if (flippedLower) {
this.tickManager.clear(tickLower);
}
if (flippedUpper) {
this.tickManager.clear(tickUpper);
}
}
return position;
}
getTickMap(): Map<number, TickView> {
return this.tickManager.sortedTicks;
}
getTick(tick: number): TickView {
return this.tickManager.getTickReadonly(tick);
}
getPosition(
owner: string,
tickLower: number,
tickUpper: number
): PositionView {
return this.positionManager.getPositionReadonly(
owner,
tickLower,
tickUpper
);
}
getPositionBalance(
owner: string,
tickLower: number,
tickUpper: number
): {
amount0: JSBI;
amount1: JSBI;
} {
let position = this.getPosition(owner, tickLower, tickUpper);
let amount0: JSBI = ZERO;
let amount1: JSBI = ZERO;
if (this.tickCurrent < tickLower) {
amount0 = SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
position.liquidity
);
} else if (this.tickCurrent < tickUpper) {
amount0 = SqrtPriceMath.getAmount0Delta(
this._sqrtPriceX96,
TickMath.getSqrtRatioAtTick(tickUpper),
position.liquidity
);
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
this._sqrtPriceX96,
position.liquidity
);
} else {
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(tickLower),
TickMath.getSqrtRatioAtTick(tickUpper),
position.liquidity
);
}
return {
amount0,
amount1,
};
}
toString(): string {
return `
Current State:
token0Balance: ${this.token0Balance.toString()}
token1Balance: ${this.token1Balance.toString()}
sqrtPriceX96: ${this.sqrtPriceX96.toString()}
liquidity: ${this.liquidity.toString()}
tickCurrent: ${this.tickCurrent}
feeGrowthGlobal0X128: ${this.feeGrowthGlobal0X128.toString()}
feeGrowthGlobal1X128: ${this.feeGrowthGlobal1X128.toString()}
`;
}
}
================================================
FILE: src/core/index.ts
================================================
export * from "./CorePool";
================================================
FILE: src/entity/EndBlockType.ts
================================================
export type EndBlockTypeWhenInit =
| number
| "latest"
| "afterDeployment"
| "afterInitialization";
export type EndBlockTypeWhenRecover =
| number
| "latestOnChain"
| "latestDownloaded"
| "afterDeployment"
| "afterInitialization";
================================================
FILE: src/entity/LiquidityEvent.ts
================================================
import { EventType } from "../enum/EventType";
import JSBI from "jsbi";
export interface LiquidityEvent {
id: number;
type: EventType.MINT | EventType.BURN;
msgSender: string;
recipient: string;
liquidity: JSBI;
amount0: JSBI;
amount1: JSBI;
tickLower: number;
tickUpper: number;
blockNumber: number;
transactionHash: string;
logIndex: number;
date: Date;
verified: boolean;
}
================================================
FILE: src/entity/Record.ts
================================================
import { ActionType } from "../enum/ActionType";
import {
BurnParams,
CollectParams,
ForkParams,
GeneralReturnParams,
InitializeParams,
MintParams,
SwapParams,
VoidReturnParams,
} from "../interface/ActionParams";
export type Record = {
id: string;
actionType: ActionType;
actionParams:
| InitializeParams
| MintParams
| BurnParams
| SwapParams
| CollectParams
| ForkParams;
actionReturnValues: GeneralReturnParams | VoidReturnParams;
timestamp: Date;
};
================================================
FILE: src/entity/Snapshot.ts
================================================
import JSBI from "jsbi";
import { PositionManager } from "../manager/PositionManager";
import { TickManager } from "../manager/TickManager";
import { PoolConfig } from "../model/PoolConfig";
export type Snapshot = {
id: string;
description: string;
poolConfig: PoolConfig;
token0Balance: JSBI;
token1Balance: JSBI;
sqrtPriceX96: JSBI;
liquidity: JSBI;
tickCurrent: number;
feeGrowthGlobal0X128: JSBI;
feeGrowthGlobal1X128: JSBI;
tickManager: TickManager;
positionManager: PositionManager;
timestamp: Date;
};
================================================
FILE: src/entity/SnapshotProfile.ts
================================================
export type SnapshotProfile = {
id: string;
description: string;
};
================================================
FILE: src/entity/StepComputations.ts
================================================
import JSBI from "jsbi";
export type StepComputations = {
sqrtPriceStartX96: JSBI;
tickNext: number;
initialized: boolean;
sqrtPriceNextX96: JSBI;
amountIn: JSBI;
amountOut: JSBI;
feeAmount: JSBI;
};
================================================
FILE: src/entity/SwapEvent.ts
================================================
import { EventType } from "../enum/EventType";
import JSBI from "jsbi";
export interface SwapEvent {
id: number;
type: EventType.SWAP;
msgSender: string;
recipient: string;
amount0: JSBI;
amount1: JSBI;
amountSpecified: JSBI;
sqrtPriceX96: JSBI;
liquidity: JSBI;
tick: number;
blockNumber: number;
transactionHash: string;
logIndex: number;
date: Date;
verified: boolean;
}
================================================
FILE: src/entity/index.ts
================================================
export * from "./Record";
export * from "./Snapshot";
export * from "./SnapshotProfile";
export * from "./EndBlockType";
================================================
FILE: src/enum/ActionType.ts
================================================
export enum ActionType {
INITIALIZE = "initialize",
MINT = "mint",
BURN = "burn",
COLLECT = "collect",
SWAP = "swap",
FORK = "fork",
}
================================================
FILE: src/enum/EventDataSourceType.ts
================================================
export enum EventDataSourceType {
SUBGRAPH = 1,
RPC = 2,
}
================================================
FILE: src/enum/EventType.ts
================================================
export enum EventType {
MINT = 1,
BURN = 2,
SWAP = 3,
}
================================================
FILE: src/enum/FeeAmount.ts
================================================
/**
* The default factory enabled fee amounts, denominated in hundredths of bips.
*/
export enum FeeAmount {
EXTRA_LOW = 100,
LOW = 500,
MEDIUM = 3000,
HIGH = 10000,
}
================================================
FILE: src/enum/InternalConstants.ts
================================================
import JSBI from "jsbi";
import { FeeAmount } from "./FeeAmount";
import dotenv from "dotenv";
// load .env file
dotenv.config();
export const SUBGRAPH_API_KEY = process.env.SUBGRAPH_API_KEY;
// constants used internally but not expected to be used externally
export const UNISWAP_V3_SUBGRAPH_ENDPOINT =
"https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3";
export const BSC_PA
gitextract_2ntasox1/ ├── .github/ │ └── workflows/ │ ├── lint-from-fork.yml │ └── unit-tests-from-fork.yml ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── SUMMARY.md ├── abi/ │ ├── OHM.json │ ├── UniswapV3Pool2.json │ └── Visor.json ├── docs/ │ ├── about-core-pool-config.md │ ├── building-a-client-instance.md │ ├── configuration.md │ ├── contributing.md │ ├── fetching-all-the-data-of-a-certain-pool-from-ethereum.md │ ├── forking-and-retracing.md │ ├── getting-a-core-pool-instance.md │ ├── getting-a-pool-instance-with-the-data-fetched.md │ ├── how-tuner-library-works.md │ ├── installing-tuner.md │ ├── interacting-with-core-pool.md │ ├── loading-and-streaming-events-into-a-pool.md │ ├── performance.md │ ├── persisting-and-recovering.md │ ├── pool-state-and-transition.md │ ├── post-processor.md │ ├── quick-start.md │ └── simulator-roadmap-manager.md ├── examples/ │ ├── Uniswap-v3-Events-Downloader/ │ │ ├── EventsDownloader.ts │ │ └── EventsUpdater.ts │ └── Uniswap-v3-Strategy-Backtest/ │ └── README.md ├── hardhat.config.ts ├── package.json ├── src/ │ ├── client/ │ │ ├── BSCDataDownloader.ts │ │ ├── SimulatorClient.ts │ │ └── index.ts │ ├── config/ │ │ └── TunerConfig.ts │ ├── core/ │ │ ├── ConfigurableCorePool.ts │ │ ├── CorePool.ts │ │ └── index.ts │ ├── entity/ │ │ ├── EndBlockType.ts │ │ ├── LiquidityEvent.ts │ │ ├── Record.ts │ │ ├── Snapshot.ts │ │ ├── SnapshotProfile.ts │ │ ├── StepComputations.ts │ │ ├── SwapEvent.ts │ │ └── index.ts │ ├── enum/ │ │ ├── ActionType.ts │ │ ├── EventDataSourceType.ts │ │ ├── EventType.ts │ │ ├── FeeAmount.ts │ │ ├── InternalConstants.ts │ │ └── index.ts │ ├── index.ts │ ├── interface/ │ │ ├── ActionParams.ts │ │ ├── ConfigurableCorePool.ts │ │ ├── CorePoolView.ts │ │ ├── PoolStateContainer.ts │ │ ├── PoolStateView.ts │ │ ├── PositionView.ts │ │ ├── SimulationDataManager.ts │ │ ├── SimulatorRoadmapManager.ts │ │ ├── SimulatorVisitor.ts │ │ ├── TickView.ts │ │ ├── Transition.ts │ │ ├── Visitable.ts │ │ └── index.ts │ ├── manager/ │ │ ├── EventDBManager.ts │ │ ├── PositionManager.ts │ │ ├── SQLiteSimulationDataManager.ts │ │ ├── SimulatorConsoleVisitor.ts │ │ ├── SimulatorPersistenceVisitor.ts │ │ ├── SimulatorRoadmapManager.ts │ │ ├── TickManager.ts │ │ └── index.ts │ ├── model/ │ │ ├── PoolConfig.ts │ │ ├── PoolState.ts │ │ ├── Position.ts │ │ ├── Roadmap.ts │ │ ├── Tick.ts │ │ ├── Transition.ts │ │ └── index.ts │ └── util/ │ ├── BNUtils.ts │ ├── DateConverter.ts │ ├── DateUtils.ts │ ├── FileUtils.ts │ ├── FullMath.ts │ ├── IdGenerator.ts │ ├── LiquidityMath.ts │ ├── PoolStateHelper.ts │ ├── Serializer.ts │ ├── SqrtPriceMath.ts │ ├── SwapMath.ts │ ├── TickMath.ts │ └── index.ts ├── test/ │ ├── ConfigurableCorePool.test.ts │ ├── JSBI.test.ts │ ├── LiquidityMath.test.ts │ ├── Serializer.test.ts │ ├── SimulationDataManager.test.ts │ ├── SimulatorClient.test.ts │ ├── SimulatorRoadmapManager.test.ts │ ├── SwapMath.spec.ts │ ├── TestSubgraph.test.ts │ ├── Tick.spec.ts │ ├── TickManager.test.ts │ ├── TickMath.test.ts │ ├── UniswapV3Pool.spec.ts.pending │ ├── UniswapV3Pool.swaps.spec.ts.pending │ ├── contracts/ │ │ ├── CorePool.test.ts │ │ ├── Ticks.test.ts │ │ └── src/ │ │ └── contracts/ │ │ ├── NoDelegateCall.sol │ │ ├── UniswapV3Factory2.sol │ │ ├── UniswapV3Pool2.sol │ │ ├── UniswapV3PoolDeployer2.sol │ │ ├── interfaces/ │ │ │ ├── IERC20Minimal.sol │ │ │ ├── IUniswapV3Factory.sol │ │ │ ├── IUniswapV3Pool.sol │ │ │ ├── IUniswapV3PoolDeployer.sol │ │ │ ├── LICENSE │ │ │ ├── callback/ │ │ │ │ ├── IUniswapV3FlashCallback.sol │ │ │ │ ├── IUniswapV3MintCallback.sol │ │ │ │ └── IUniswapV3SwapCallback.sol │ │ │ └── pool/ │ │ │ ├── IUniswapV3PoolActions.sol │ │ │ ├── IUniswapV3PoolDerivedState.sol │ │ │ ├── IUniswapV3PoolEvents.sol │ │ │ ├── IUniswapV3PoolImmutables.sol │ │ │ ├── IUniswapV3PoolOwnerActions.sol │ │ │ └── IUniswapV3PoolState.sol │ │ └── libraries/ │ │ ├── BitMath.sol │ │ ├── FixedPoint128.sol │ │ ├── FixedPoint96.sol │ │ ├── FullMath.sol │ │ ├── LICENSE_GPL │ │ ├── LICENSE_MIT │ │ ├── LiquidityMath.sol │ │ ├── LowGasSafeMath.sol │ │ ├── Oracle.sol │ │ ├── Position.sol │ │ ├── SafeCast.sol │ │ ├── SqrtPriceMath.sol │ │ ├── SwapMath.sol │ │ ├── Tick.sol │ │ ├── TickBitmap.sol │ │ ├── TickMath.sol │ │ ├── TransferHelper.sol │ │ └── UnsafeMath.sol │ ├── shared/ │ │ ├── MockableTick.ts │ │ ├── checkObservationEquals.ts │ │ ├── expect.ts │ │ ├── fixtures.ts │ │ ├── format.ts │ │ ├── snapshotGasCost.ts │ │ └── utilities.ts │ └── stubs/ │ ├── MockTimeUniswapV3Pool.ts │ ├── TestERC20.d.ts │ ├── TestUniswapV3Callee.d.ts │ ├── TestUniswapV3ReentrantCallee.d.ts │ ├── TestUniswapV3SwapPay.d.ts │ ├── TickTest.ts │ └── UniswapV3Factory.d.ts ├── tsconfig.json └── tuner.config.js
SYMBOL INDEX (481 symbols across 73 files)
FILE: examples/Uniswap-v3-Events-Downloader/EventsDownloader.ts
function main (line 19) | async function main() {
FILE: examples/Uniswap-v3-Events-Downloader/EventsUpdater.ts
function main (line 23) | async function main() {
FILE: src/client/BSCDataDownloader.ts
class BSCDataDownloader (line 46) | class BSCDataDownloader {
method constructor (line 51) | constructor(
method queryDeploymentBlockNumber (line 63) | async queryDeploymentBlockNumber(poolAddress: string): Promise<number> {
method queryInitializationBlockNumber (line 70) | async queryInitializationBlockNumber(poolAddress: string): Promise<num...
method parseEndBlockTypeWhenInit (line 80) | async parseEndBlockTypeWhenInit(
method parseEndBlockTypeWhenRecover (line 97) | async parseEndBlockTypeWhenRecover(
method generateMainnetEventDBFilePath (line 117) | generateMainnetEventDBFilePath(
method parseFromMainnetEventDBFilePath (line 124) | parseFromMainnetEventDBFilePath(filePath: string): {
method download (line 133) | async download(
method replaceLiquidityEventsFromSubgraphToRPC (line 221) | async replaceLiquidityEventsFromSubgraphToRPC(
method update (line 264) | async update(
method preProcessEvents (line 381) | async preProcessEvents(poolAddress: string, eventDB: EventDBManager) {
method fixBurnEvents (line 394) | async fixBurnEvents(
method initializeAndReplayEvents (line 422) | async initializeAndReplayEvents(
method downloadEventsFromSubgraph (line 477) | private async downloadEventsFromSubgraph(
method fixBurnEventsFromRPC (line 532) | private async fixBurnEventsFromRPC(
method downloadEventsFromRPC (line 599) | private async downloadEventsFromRPC(
method queryFilterWithRetry (line 640) | private async queryFilterWithRetry(
method request (line 692) | private async request(query: string) {
method saveEventsFromSubgraph (line 803) | private async saveEventsFromSubgraph(
method pullBurnEventsFromRPC (line 1041) | private async pullBurnEventsFromRPC(
method saveEventsFromRPC (line 1068) | private async saveEventsFromRPC(
method preProcessSwapEvent (line 1176) | async preProcessSwapEvent(eventDB: EventDBManager) {
method nextBatch (line 1210) | private nextBatch(currBlock: number) {
method getCorePoolContarct (line 1215) | private async getCorePoolContarct(
method getAndSortEventByBlock (line 1221) | private async getAndSortEventByBlock(
method replayEventsAndAssertReturnValues (line 1254) | private async replayEventsAndAssertReturnValues(
FILE: src/client/SimulatorClient.ts
class SimulatorClient (line 22) | class SimulatorClient {
method simulatorRoadmapManager (line 26) | public get simulatorRoadmapManager(): ISimulatorRoadmapManager {
method constructor (line 30) | constructor(simulatorDBManager: SimulationDataManager) {
method recoverFromMainnetEventDBFile (line 81) | async recoverFromMainnetEventDBFile(
method initCorePoolFromConfig (line 119) | initCorePoolFromConfig(poolConfig: PoolConfig): IConfigurableCorePool {
method initCorePoolFromOnchainData (line 128) | initCorePoolFromOnchainData(
method recoverCorePoolFromSnapshot (line 154) | recoverCorePoolFromSnapshot(
method listSnapshotProfiles (line 171) | listSnapshotProfiles(): Promise<SnapshotProfile[]> {
method shutdown (line 175) | shutdown(): Promise<void> {
method getSnapshot (line 179) | private getSnapshot(snapshotId: string): Promise<Snapshot | undefined> {
FILE: src/config/TunerConfig.ts
type TunerConfig (line 4) | interface TunerConfig {
function loadConfig (line 8) | function loadConfig(file?: string): TunerConfig {
function mergeDeep (line 31) | function mergeDeep(target: any, source: any) {
FILE: src/core/ConfigurableCorePool.ts
class ConfigurableCorePool (line 32) | class ConfigurableCorePool implements IConfigurableCorePool, Visitable {
method constructor (line 44) | constructor(
method getPoolState (line 77) | getPoolState(): PoolStateView {
method getCorePool (line 81) | getCorePool(): CorePoolView {
method initialize (line 85) | initialize(
method setCoreGlobalState (line 107) | setCoreGlobalState(sqrtPriceX96: JSBI, liquidity: JSBI, tickCurrent: n...
method rapidMint (line 111) | rapidMint(
method phantomMint (line 148) | phantomMint(
method mint (line 186) | mint(
method rapidBurn (line 223) | rapidBurn(
method phantomBurn (line 260) | phantomBurn(
method burn (line 298) | burn(
method collect (line 335) | collect(
method swap (line 380) | swap(
method querySwap (line 415) | querySwap(
method resolveInputFromSwapResultEvent (line 425) | async resolveInputFromSwapResultEvent(
method updatePostProcessor (line 520) | updatePostProcessor(
method takeSnapshot (line 529) | takeSnapshot(description: string): boolean {
method fork (line 546) | fork(): IConfigurableCorePool {
method persistSnapshot (line 556) | persistSnapshot(): Promise<string> {
method stepBack (line 564) | stepBack() {
method recover (line 572) | recover(poolStateId: string) {
method poolState (line 582) | get poolState(): PoolState {
method accept (line 586) | accept(visitor: SimulatorVisitor): Promise<string> {
method showStateTransitionRoute (line 591) | showStateTransitionRoute(
method persistSnapshots (line 604) | persistSnapshots(
method traversePoolStateChain (line 620) | private traversePoolStateChain(
method handleSingleStepOnChain (line 647) | private handleSingleStepOnChain(
method buildRecord (line 681) | private buildRecord(
method getNextPoolState (line 695) | private getNextPoolState(fromTransition: Transition) {
method postProcess (line 705) | private postProcess(
FILE: src/core/CorePool.ts
class CorePool (line 24) | class CorePool {
method constructor (line 40) | constructor(
method token0Balance (line 72) | public get token0Balance(): JSBI {
method token1Balance (line 76) | public get token1Balance(): JSBI {
method sqrtPriceX96 (line 80) | public get sqrtPriceX96(): JSBI {
method liquidity (line 84) | public get liquidity(): JSBI {
method tickCurrent (line 88) | public get tickCurrent(): number {
method feeGrowthGlobal0X128 (line 92) | public get feeGrowthGlobal0X128(): JSBI {
method feeGrowthGlobal1X128 (line 96) | public get feeGrowthGlobal1X128(): JSBI {
method tickManager (line 100) | public get tickManager(): TickManager {
method positionManager (line 104) | public get positionManager(): PositionManager {
method initialize (line 108) | initialize(sqrtPriceX96: JSBI) {
method setCoreGlobalState (line 121) | setCoreGlobalState(sqrtPriceX96: JSBI, liquidity: JSBI, tickCurrent: n...
method phantomMint (line 127) | phantomMint(
method rapidMint (line 153) | rapidMint(
method mint (line 173) | mint(
method phantomBurn (line 199) | phantomBurn(
method rapidBurn (line 228) | rapidBurn(
method burn (line 248) | burn(
method collect (line 277) | collect(
method querySwap (line 300) | querySwap(
method swap (line 313) | swap(
method handleSwap (line 326) | private handleSwap(
method checkTicks (line 521) | private checkTicks(tickLower: number, tickUpper: number) {
method phantomModifyPosition (line 533) | private phantomModifyPosition(
method rapidModifyPosition (line 602) | private rapidModifyPosition(
method modifyPosition (line 656) | private modifyPosition(
method phantomUpdatePosition (line 730) | private phantomUpdatePosition(
method rapidUpdatePosition (line 759) | private rapidUpdatePosition(
method updatePosition (line 801) | private updatePosition(
method getTickMap (line 866) | getTickMap(): Map<number, TickView> {
method getTick (line 870) | getTick(tick: number): TickView {
method getPosition (line 874) | getPosition(
method getPositionBalance (line 886) | getPositionBalance(
method toString (line 929) | toString(): string {
FILE: src/entity/EndBlockType.ts
type EndBlockTypeWhenInit (line 1) | type EndBlockTypeWhenInit =
type EndBlockTypeWhenRecover (line 7) | type EndBlockTypeWhenRecover =
FILE: src/entity/LiquidityEvent.ts
type LiquidityEvent (line 4) | interface LiquidityEvent {
FILE: src/entity/Record.ts
type Record (line 13) | type Record = {
FILE: src/entity/Snapshot.ts
type Snapshot (line 6) | type Snapshot = {
FILE: src/entity/SnapshotProfile.ts
type SnapshotProfile (line 1) | type SnapshotProfile = {
FILE: src/entity/StepComputations.ts
type StepComputations (line 3) | type StepComputations = {
FILE: src/entity/SwapEvent.ts
type SwapEvent (line 4) | interface SwapEvent {
FILE: src/enum/ActionType.ts
type ActionType (line 1) | enum ActionType {
FILE: src/enum/EventDataSourceType.ts
type EventDataSourceType (line 1) | enum EventDataSourceType {
FILE: src/enum/EventType.ts
type EventType (line 1) | enum EventType {
FILE: src/enum/FeeAmount.ts
type FeeAmount (line 4) | enum FeeAmount {
FILE: src/enum/InternalConstants.ts
constant SUBGRAPH_API_KEY (line 9) | const SUBGRAPH_API_KEY = process.env.SUBGRAPH_API_KEY;
constant UNISWAP_V3_SUBGRAPH_ENDPOINT (line 12) | const UNISWAP_V3_SUBGRAPH_ENDPOINT =
constant BSC_PANCAKE_V3_SUBGRAPH_ENDPOINT (line 15) | const BSC_PANCAKE_V3_SUBGRAPH_ENDPOINT = `https://gateway.thegraph.com/a...
constant NEGATIVE_ONE (line 17) | const NEGATIVE_ONE = JSBI.BigInt(-1);
constant ZERO (line 18) | const ZERO = JSBI.BigInt(0);
constant ONE (line 19) | const ONE = JSBI.BigInt(1);
constant TWO (line 20) | const TWO = JSBI.BigInt(2);
constant Q32 (line 42) | const Q32 = JSBI.exponentiate(TWO, JSBI.BigInt(32));
constant Q96 (line 43) | const Q96 = JSBI.exponentiate(TWO, JSBI.BigInt(96));
constant Q128 (line 44) | const Q128 = JSBI.exponentiate(TWO, JSBI.BigInt(128));
constant Q192 (line 45) | const Q192 = JSBI.exponentiate(Q96, TWO);
constant MAX_FEE (line 48) | const MAX_FEE = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(6));
constant PROTOCOL_FEE (line 50) | const PROTOCOL_FEE = JSBI.BigInt(3300);
constant PROTOCOL_FEE_DENOMINATOR (line 51) | const PROTOCOL_FEE_DENOMINATOR = JSBI.BigInt(10000);
constant TICK_SPACINGS (line 54) | const TICK_SPACINGS: { [amount in FeeAmount]: number } = {
constant RAPID_POSITION_OWNER (line 61) | const RAPID_POSITION_OWNER =
FILE: src/interface/ActionParams.ts
type MethodParams (line 4) | type MethodParams =
type InitializeParams (line 12) | interface InitializeParams {
type MintParams (line 17) | interface MintParams {
type BurnParams (line 25) | interface BurnParams {
type SwapParams (line 33) | interface SwapParams {
type CollectParams (line 40) | interface CollectParams {
type ForkParams (line 49) | interface ForkParams {
type ReturnParams (line 53) | type ReturnParams = VoidReturnParams | GeneralReturnParams;
type VoidReturnParams (line 55) | interface VoidReturnParams {}
type GeneralReturnParams (line 57) | interface GeneralReturnParams {
FILE: src/interface/ConfigurableCorePool.ts
type ConfigurableCorePool (line 7) | interface ConfigurableCorePool {
FILE: src/interface/CorePoolView.ts
type CorePoolView (line 3) | type CorePoolView = Pick<
FILE: src/interface/PoolStateContainer.ts
type PoolStateContainer (line 3) | interface PoolStateContainer {
FILE: src/interface/PoolStateView.ts
type PoolStateView (line 3) | type PoolStateView = Pick<
FILE: src/interface/PositionView.ts
type PositionView (line 3) | type PositionView = Pick<
FILE: src/interface/SimulationDataManager.ts
type SimulationDataManager (line 7) | interface SimulationDataManager {
FILE: src/interface/SimulatorRoadmapManager.ts
type SimulatorRoadmapManager (line 3) | interface SimulatorRoadmapManager {
FILE: src/interface/SimulatorVisitor.ts
type SimulatorVisitor (line 5) | interface SimulatorVisitor {
FILE: src/interface/TickView.ts
type TickView (line 3) | type TickView = Pick<
FILE: src/interface/Transition.ts
type Transition (line 4) | interface Transition {
FILE: src/interface/Visitable.ts
type Visitable (line 3) | interface Visitable {
FILE: src/manager/EventDBManager.ts
constant DATE_FORMAT (line 14) | const DATE_FORMAT: string = "YYYY-MM-DD HH:mm:ss";
type LiquidityEventRecord (line 16) | type LiquidityEventRecord = {
type SwapEventRecord (line 33) | type SwapEventRecord = {
type PoolConfigRecord (line 50) | type PoolConfigRecord = {
class EventDBManager (line 65) | class EventDBManager {
method constructor (line 68) | private constructor(dbPath: string) {
method buildInstance (line 79) | static async buildInstance(
method initTables (line 87) | initTables(): Promise<void> {
method getPoolConfig (line 171) | getPoolConfig(): Promise<PoolConfig | undefined> {
method getInitializationEventBlockNumber (line 185) | getInitializationEventBlockNumber(): Promise<number> {
method getLatestEventBlockNumber (line 197) | getLatestEventBlockNumber(): Promise<number> {
method getInitialSqrtPriceX96 (line 203) | getInitialSqrtPriceX96(): Promise<JSBI> {
method getLiquidityEventsByDate (line 215) | getLiquidityEventsByDate(
method getSwapEventsByDate (line 231) | getSwapEventsByDate(
method getLiquidityEventsByBlockNumber (line 245) | getLiquidityEventsByBlockNumber(
method deleteLiquidityEventsByBlockNumber (line 264) | deleteLiquidityEventsByBlockNumber(
method getSwapEventsByBlockNumber (line 278) | getSwapEventsByBlockNumber(
method getBurnEventsByTransactionHash (line 292) | getBurnEventsByTransactionHash(
method deleteSwapEventsByBlockNumber (line 305) | deleteSwapEventsByBlockNumber(
method addPoolConfig (line 317) | addPoolConfig(poolConfig: PoolConfig) {
method addAmountSpecified (line 325) | addAmountSpecified(id: number, amountSpecified: string): Promise<numbe...
method addInitialSqrtPriceX96 (line 333) | addInitialSqrtPriceX96(initialSqrtPriceX96: string): Promise<number> {
method saveLatestEventBlockNumber (line 341) | saveLatestEventBlockNumber(latestEventBlockNumber: number): Promise<nu...
method saveInitializationEventBlockNumber (line 349) | saveInitializationEventBlockNumber(
method saveBurnEvent (line 360) | saveBurnEvent(
method getLatestVerifiedSwapBlockNumber (line 383) | getLatestVerifiedSwapBlockNumber(): Promise<number> {
method getLatestVerifiedBurnBlockNumber (line 395) | getLatestVerifiedBurnBlockNumber(): Promise<number> {
method saveLatestVerifiedSwapBlockNumber (line 407) | saveLatestVerifiedSwapBlockNumber(
method saveLatestVerifiedBurnBlockNumber (line 418) | saveLatestVerifiedBurnBlockNumber(
method markLiquidityEventAsVerified (line 434) | markLiquidityEventAsVerified(eventId: number): Promise<number> {
method markSwapEventAsVerified (line 447) | markSwapEventAsVerified(eventId: number): Promise<number> {
method markLiquidityEventsAsVerified (line 460) | markLiquidityEventsAsVerified(eventIds: number[]): Promise<number> {
method markSwapEventsAsVerified (line 476) | markSwapEventsAsVerified(eventIds: number[]): Promise<number> {
method getUnverifiedLiquidityEvents (line 494) | getUnverifiedLiquidityEvents(
method getUnverifiedSwapEvents (line 520) | getUnverifiedSwapEvents(
method insertLiquidityEvent (line 537) | insertLiquidityEvent(
method batchInsertLiquidityEvents (line 580) | async batchInsertLiquidityEvents(
method insertSwapEvent (line 669) | insertSwapEvent(
method batchInsertSwapEvents (line 709) | async batchInsertSwapEvents(
method close (line 795) | close(): Promise<void> {
method readPoolConfig (line 799) | private readPoolConfig(
method queryLiquidityEventsByDate (line 805) | private queryLiquidityEventsByDate(
method querySwapEventsByDate (line 817) | private querySwapEventsByDate(
method queryLiquidityEventsByBlockNumber (line 827) | private queryLiquidityEventsByBlockNumber(
method querySwapEventsByBlockNumber (line 839) | private querySwapEventsByBlockNumber(
method queryBurnEventsByTransactionHash (line 849) | private queryBurnEventsByTransactionHash(
method insertPoolConfig (line 859) | private insertPoolConfig(
method updateAmountSpecified (line 879) | private updateAmountSpecified(
method updateInitialSqrtPriceX96 (line 892) | private updateInitialSqrtPriceX96(
method updateLatestEventBlockNumber (line 901) | private updateLatestEventBlockNumber(
method updateInitializationEventBlockNumber (line 910) | private updateInitializationEventBlockNumber(
method updateLatestVerifiedSwapBlockNumber (line 922) | private updateLatestVerifiedSwapBlockNumber(
method updateLatestVerifiedBurnBlockNumber (line 934) | private updateLatestVerifiedBurnBlockNumber(
method updateBurnEvent (line 946) | private updateBurnEvent(
method migratePoolConfigTable (line 974) | private async migratePoolConfigTable(): Promise<void> {
method migrateEventTables (line 1020) | private async migrateEventTables(): Promise<void> {
method deserializeLiquidityEvent (line 1096) | private deserializeLiquidityEvent(
method deserializeSwapEvent (line 1117) | private deserializeSwapEvent(event: SwapEventRecord): SwapEvent {
method getBuilderContext (line 1137) | private getBuilderContext(
FILE: src/manager/PositionManager.ts
class PositionManager (line 9) | class PositionManager {
method constructor (line 15) | constructor(positions: Map<string, Position> = new Map()) {
method getKey (line 20) | static getKey(owner: string, tickLower: number, tickUpper: number): st...
method extractFromKey (line 26) | static extractFromKey(key: string): {
method set (line 39) | set(owner: string, key: string, position: Position) {
method clear (line 48) | clear(key: string) {
method getPositionAndInitIfAbsent (line 59) | getPositionAndInitIfAbsent(
method getPositionReadonly (line 71) | getPositionReadonly(
method collectPosition (line 81) | collectPosition(
FILE: src/manager/SQLiteSimulationDataManager.ts
constant DATE_FORMAT (line 20) | const DATE_FORMAT: string = "YYYY-MM-DD HH:mm:ss.SSS";
type RoadmapRecord (line 22) | type RoadmapRecord = {
type SnapshotRecord (line 30) | type SnapshotRecord = {
type PoolConfigRecord (line 47) | type PoolConfigRecord = {
class SQLiteSimulationDataManager (line 57) | class SQLiteSimulationDataManager implements SimulationDataManager {
method constructor (line 60) | private constructor(dbPath: string) {
method buildInstance (line 72) | static async buildInstance(
method initTables (line 80) | initTables(): Promise<void> {
method persistRoadmap (line 137) | persistRoadmap(roadmap: Roadmap): Promise<number> {
method persistSnapshot (line 151) | persistSnapshot(poolState: PoolState): Promise<number> {
method getSnapshotProfiles (line 183) | getSnapshotProfiles(): Promise<SnapshotProfile[]> {
method getSnapshots (line 196) | getSnapshots(snapshotIds: number[]): Promise<Snapshot[]> {
method getSnapshot (line 220) | getSnapshot(snapshotId: string): Promise<Snapshot | undefined> {
method getPoolConfig (line 242) | getPoolConfig(poolConfigId: string): Promise<PoolConfig | undefined> {
method getRoadmap (line 256) | getRoadmap(roadmapId: string): Promise<Roadmap | undefined> {
method close (line 269) | close(): Promise<void> {
method readSnapshot (line 273) | private readSnapshot(
method readSnapshots (line 282) | private readSnapshots(
method readSnapshotProfiles (line 289) | private readSnapshotProfiles(
method insertSnapshot (line 297) | private insertSnapshot(
method insertRoadmap (line 332) | private insertRoadmap(
method readRoadmap (line 349) | private readRoadmap(
method readPoolConfig (line 358) | private readPoolConfig(
method insertPoolConfig (line 367) | private insertPoolConfig(
method deserializeSnapshot (line 383) | private deserializeSnapshot(
method getBuilderContext (line 407) | private getBuilderContext(
FILE: src/manager/SimulatorConsoleVisitor.ts
class SimulatorConsoleVisitor (line 11) | class SimulatorConsoleVisitor implements SimulatorVisitor {
method visitTransition (line 12) | visitTransition(
method visitPoolState (line 21) | visitPoolState(
method visitConfigurableCorePool (line 31) | visitConfigurableCorePool(
FILE: src/manager/SimulatorPersistenceVisitor.ts
class SimulatorPersistenceVisitor (line 7) | class SimulatorPersistenceVisitor implements SimulatorVisitor {
method constructor (line 10) | constructor(dbManager: SimulationDataManager) {
method visitTransition (line 14) | visitTransition(transition: Transition): Promise<string> {
method visitPoolState (line 18) | visitPoolState(
method visitConfigurableCorePool (line 31) | visitConfigurableCorePool(
FILE: src/manager/SimulatorRoadmapManager.ts
class SimulatorRoadmapManager (line 15) | class SimulatorRoadmapManager
method constructor (line 22) | constructor(dbManager: SimulationDataManager) {
method addPoolState (line 27) | addPoolState(poolState: PoolState): string {
method getPoolState (line 32) | getPoolState(poolStateId: string): PoolState | undefined {
method hasPoolState (line 36) | hasPoolState(poolStateId: string): boolean {
method addRoute (line 40) | addRoute(configurableCorePool: ConfigurableCorePool) {
method printRoute (line 47) | printRoute(configurableCorePoolId: string): Promise<void> {
method listRoutes (line 55) | listRoutes(): Array<ConfigurableCorePool> {
method persistRoute (line 59) | persistRoute(
method loadAndPrintRoute (line 78) | loadAndPrintRoute(roadmapId: string): Promise<void> {
FILE: src/manager/TickManager.ts
class TickManager (line 9) | class TickManager {
method sortedTicks (line 13) | public get sortedTicks(): Map<number, Tick> {
method constructor (line 17) | constructor(ticks: Map<number, Tick> = new Map()) {
method getTickAndInitIfAbsent (line 22) | getTickAndInitIfAbsent(tickIndex: number): Tick {
method getTickReadonly (line 31) | getTickReadonly(tickIndex: number): TickView {
method set (line 37) | set(tick: Tick) {
method nextInitializedTick (line 42) | private nextInitializedTick(
method getNextInitializedTick (line 67) | getNextInitializedTick(
method getFeeGrowthInside (line 107) | getFeeGrowthInside(
method clear (line 163) | clear(tick: number) {
method sortTicks (line 168) | private sortTicks() {
method getSortedTicks (line 175) | private getSortedTicks(): Tick[] {
method isBelowSmallest (line 179) | private isBelowSmallest(sortedTicks: readonly Tick[], tick: number): b...
method isAtOrAboveLargest (line 184) | private isAtOrAboveLargest(
method binarySearch (line 192) | private binarySearch(sortedTicks: readonly Tick[], tick: number): numb...
FILE: src/model/PoolConfig.ts
class PoolConfig (line 4) | class PoolConfig {
method constructor (line 11) | constructor(
function toString (line 25) | function toString(poolConfig: PoolConfig): string {
FILE: src/model/PoolState.ts
constant DEFAULT_SNAPSHOT_DESCRIPTION (line 17) | const DEFAULT_SNAPSHOT_DESCRIPTION = "Automated for caching";
class PoolState (line 18) | class PoolState implements Visitable {
method constructor (line 27) | constructor(
method snapshot (line 41) | public get snapshot(): Snapshot | undefined {
method from (line 45) | static from(baseSnapshot: Snapshot): PoolState {
method takeSnapshot (line 49) | takeSnapshot(
method accept (line 84) | accept(
method recoverCorePool (line 91) | recoverCorePool(takeSnapshot?: boolean): CorePool {
method clearSnapshot (line 112) | clearSnapshot(cachingOnly: boolean = false) {
method hasSnapshot (line 121) | hasSnapshot(): boolean {
method hasBaseSnapshot (line 125) | hasBaseSnapshot(): boolean {
method addTransitionTarget (line 129) | addTransitionTarget(record: Record): Transition {
method getTransitionSource (line 135) | getTransitionSource(): TransitionView | undefined {
method getTransitionTargets (line 139) | getTransitionTargets(): TransitionView[] {
method fork (line 143) | fork(): PoolState {
FILE: src/model/Position.ts
class Position (line 10) | class Position {
method liquidity (line 22) | public get liquidity(): JSBI {
method feeGrowthInside0LastX128 (line 26) | public get feeGrowthInside0LastX128(): JSBI {
method feeGrowthInside1LastX128 (line 30) | public get feeGrowthInside1LastX128(): JSBI {
method tokensOwed0 (line 34) | public get tokensOwed0(): JSBI {
method tokensOwed1 (line 38) | public get tokensOwed1(): JSBI {
method update (line 42) | update(
method updateBurn (line 78) | updateBurn(newTokensOwed0: JSBI, newTokensOwed1: JSBI) {
method isEmpty (line 83) | isEmpty(): boolean {
FILE: src/model/Roadmap.ts
class Roadmap (line 3) | class Roadmap {
method constructor (line 9) | constructor(description: string, snapshots: number[]) {
function toString (line 17) | function toString(roadmap: Roadmap): string {
FILE: src/model/Tick.ts
class Tick (line 16) | class Tick {
method constructor (line 44) | constructor(
method fromOnchainData (line 66) | static fromOnchainData(
method tickIndex (line 82) | public get tickIndex(): number {
method liquidityGross (line 86) | public get liquidityGross(): JSBI {
method liquidityNet (line 90) | public get liquidityNet(): JSBI {
method feeGrowthOutside0X128 (line 94) | public get feeGrowthOutside0X128(): JSBI {
method feeGrowthOutside1X128 (line 98) | public get feeGrowthOutside1X128(): JSBI {
method initialized (line 102) | public get initialized(): boolean {
method update (line 106) | update(
method cross (line 138) | cross(feeGrowthGlobal0X128: JSBI, feeGrowthGlobal1X128: JSBI): JSBI {
FILE: src/model/Transition.ts
class Transition (line 9) | class Transition implements Visitable, TransitionView {
method source (line 14) | public get source(): PoolState {
method target (line 18) | public get target(): PoolState | undefined {
method target (line 22) | public set target(value: PoolState | undefined) {
method record (line 26) | public get record(): Record {
method constructor (line 30) | constructor(source: PoolState, record: Record) {
method getSource (line 35) | getSource(): PoolStateView {
method getTarget (line 39) | getTarget(): PoolStateView {
method getRecord (line 43) | getRecord(): Record {
method accept (line 47) | accept(visitor: SimulatorVisitor): Promise<string> {
method toString (line 51) | toString(): string {
FILE: src/util/BNUtils.ts
function sum (line 7) | function sum(bnArr: BN[]) {
function mul10pow (line 13) | function mul10pow(bn: BN, n: number) {
function div10pow (line 17) | function div10pow(bn: BN, n: number) {
function get10pow (line 21) | function get10pow(n: number) {
function isPositive (line 25) | function isPositive(bn: BN): boolean {
function toBN (line 29) | function toBN(number: any): BN {
function toJSBI (line 33) | function toJSBI(number: any): JSBI {
function convertTokenStrFromDecimal (line 37) | function convertTokenStrFromDecimal(
function convertTokenStr (line 44) | function convertTokenStr(bnStr: string): string {
FILE: src/util/DateConverter.ts
method parseDate (line 4) | static parseDate(dateStr: string): Date {
method formatDate (line 7) | static formatDate(date: Date, formatStr: string): string {
FILE: src/util/DateUtils.ts
function getDate (line 1) | function getDate(
function getYesterday (line 16) | function getYesterday(date: Date): Date {
function getTomorrow (line 20) | function getTomorrow(date: Date): Date {
function format (line 24) | function format(date: Date, fmt: string): string {
FILE: src/util/FileUtils.ts
function exists (line 4) | function exists(filePath: string): boolean {
function getDatabaseNameFromPath (line 13) | function getDatabaseNameFromPath(
FILE: src/util/FullMath.ts
method mulDiv (line 8) | static mulDiv(a: JSBI, b: JSBI, denominator: JSBI): JSBI {
method mulDivRoundingUp (line 13) | static mulDivRoundingUp(a: JSBI, b: JSBI, denominator: JSBI): JSBI {
method mod256Sub (line 24) | static mod256Sub(a: JSBI, b: JSBI): JSBI {
method equalsWithTolerance (line 37) | static equalsWithTolerance(
method sqrt (line 65) | static sqrt(value: JSBI): JSBI {
method incrTowardInfinity (line 84) | static incrTowardInfinity(value: JSBI): JSBI {
FILE: src/util/LiquidityMath.ts
method addDelta (line 7) | static addDelta(x: JSBI, y: JSBI): JSBI {
method getAmountsForLiquidity (line 20) | static getAmountsForLiquidity(
method maxLiquidityForAmounts (line 70) | static maxLiquidityForAmounts(
method maxLiquidityForAmount0Imprecise (line 109) | private static maxLiquidityForAmount0Imprecise(
method maxLiquidityForAmount0Precise (line 135) | private static maxLiquidityForAmount0Precise(
method maxLiquidityForAmount1 (line 163) | private static maxLiquidityForAmount1(
FILE: src/util/PoolStateHelper.ts
method countHistoricalPoolStateTransitions (line 16) | static countHistoricalPoolStateTransitions(poolState: PoolStateView): nu...
method recoverCorePoolByPoolStateChain (line 30) | static recoverCorePoolByPoolStateChain(poolState: PoolState): CorePool {
method buildCorePoolBySnapshot (line 51) | static buildCorePoolBySnapshot(snapshot: Snapshot): CorePool {
method buildCorePoolByOnchainData (line 75) | static buildCorePoolByOnchainData(
method buildCorePoolByPoolConfig (line 100) | static buildCorePoolByPoolConfig(poolConfig: PoolConfig): CorePool {
method applyRecordOnCorePool (line 109) | private static applyRecordOnCorePool(
method actionParamsToParamsArray (line 124) | private static actionParamsToParamsArray(actionParams: any): Array<any> {
FILE: src/util/Serializer.ts
method serialize (line 7) | static serialize<T>(rootConstructor: Constructor<T>, object: T): string {
method deserialize (line 12) | static deserialize<T>(rootConstructor: Constructor<T>, jsonStr: string):...
function printParams (line 27) | function printParams(params: object): string {
function isObject (line 42) | function isObject(value: unknown): value is object {
FILE: src/util/SqrtPriceMath.ts
function multiplyIn256 (line 12) | function multiplyIn256(x: JSBI, y: JSBI): JSBI {
function addIn256 (line 17) | function addIn256(x: JSBI, y: JSBI): JSBI {
method getAmount0Delta (line 23) | static getAmount0Delta(
method getAmount1Delta (line 45) | static getAmount1Delta(
method getNextSqrtPriceFromInput (line 67) | static getNextSqrtPriceFromInput(
method getNextSqrtPriceFromOutput (line 91) | static getNextSqrtPriceFromOutput(
method getAmount0DeltaWithRoundUp (line 115) | static getAmount0DeltaWithRoundUp(
method getAmount1DeltaWithRoundUp (line 140) | static getAmount1DeltaWithRoundUp(
method getNextSqrtPriceFromAmount0RoundingUp (line 162) | private static getNextSqrtPriceFromAmount0RoundingUp(
method getNextSqrtPriceFromAmount1RoundingDown (line 195) | private static getNextSqrtPriceFromAmount1RoundingDown(
FILE: src/util/SwapMath.ts
method computeSwapStep (line 8) | static computeSwapStep(
FILE: src/util/TickMath.ts
constant POWERS_OF_2 (line 12) | const POWERS_OF_2 = [128, 64, 32, 16, 8, 4, 2, 1].map(
function mulShift (line 19) | function mulShift(val: JSBI, mulBy: string): JSBI {
method getSqrtRatioAtTick (line 51) | static getSqrtRatioAtTick(tick: number): JSBI {
method getTickAtSqrtRatio (line 116) | static getTickAtSqrtRatio(sqrtRatioX96: JSBI): number {
method mostSignificantBit (line 180) | static mostSignificantBit(x: JSBI): number {
method tickSpacingToMaxLiquidityPerTick (line 194) | static tickSpacingToMaxLiquidityPerTick(tickSpacing: number): JSBI {
FILE: test/ConfigurableCorePool.test.ts
function getAndSortEventByDate (line 39) | async function getAndSortEventByDate(
function replayEventsAndAssertReturnValues (line 69) | async function replayEventsAndAssertReturnValues(
FILE: test/LiquidityMath.test.ts
function encodeSqrtRatioX96 (line 10) | function encodeSqrtRatioX96(amount1: number, amount0: number): JSBI {
FILE: test/SimulatorRoadmapManager.test.ts
function makeConfigurableCorePool (line 33) | async function makeConfigurableCorePool(
FILE: test/contracts/CorePool.test.ts
type FactoryFixture (line 28) | interface FactoryFixture {
type PoolFixture (line 32) | interface PoolFixture extends FactoryFixture {
function fixture (line 36) | async function fixture(wallets: Wallet[]): Promise<PoolFixture> {
function findRelatedTicks (line 157) | function findRelatedTicks(minTick: number, maxTick: number): number[] {
function findRelatedTickBitmapWordPoses (line 167) | function findRelatedTickBitmapWordPoses(ticks: number[]): number[] {
FILE: test/contracts/Ticks.test.ts
function findAvailableTicks (line 57) | function findAvailableTicks(): number[] {
FILE: test/shared/MockableTick.ts
class MockableTick (line 4) | class MockableTick extends Tick {
method updateProperties (line 8) | updateProperties(
FILE: test/shared/checkObservationEquals.ts
function checkObservationEquals (line 6) | function checkObservationEquals(
FILE: test/shared/fixtures.ts
type FactoryFixture (line 12) | interface FactoryFixture {
function factoryFixture (line 16) | async function factoryFixture(): Promise<FactoryFixture> {
type TokensFixture (line 22) | interface TokensFixture {
function tokensFixture (line 28) | async function tokensFixture(): Promise<TokensFixture> {
type TokensAndFactoryFixture (line 48) | type TokensAndFactoryFixture = FactoryFixture & TokensFixture;
type PoolFixture (line 50) | interface PoolFixture extends TokensAndFactoryFixture {
constant TEST_POOL_START_TIME (line 62) | const TEST_POOL_START_TIME = 1601906400;
FILE: test/shared/format.ts
function formatTokenAmount (line 4) | function formatTokenAmount(num: BigNumberish): string {
function formatPrice (line 10) | function formatPrice(price: BigNumberish): string {
FILE: test/shared/snapshotGasCost.ts
function snapshotGasCost (line 8) | async function snapshotGasCost(
FILE: test/shared/utilities.ts
type FeeAmount (line 42) | enum FeeAmount {
constant TICK_SPACINGS (line 48) | const TICK_SPACINGS: { [amount in FeeAmount]: number } = {
function expandTo18Decimals (line 54) | function expandTo18Decimals(n: number): JSBI {
function encodePriceSqrt (line 87) | function encodePriceSqrt(reserve1: JSBI, reserve0: JSBI): JSBI {
FILE: test/stubs/MockTimeUniswapV3Pool.ts
class MockTimeUniswapV3Pool (line 10) | class MockTimeUniswapV3Pool {
method connect (line 13) | connect(signerOrProvider: Signer | Provider | string): this {
method attach (line 16) | attach(addressOrName: string): this {
method deployed (line 19) | deployed(): Promise<this> {
method on (line 23) | on(event: EventFilter | string, listener: Listener): this {
method once (line 26) | once(event: EventFilter | string, listener: Listener): this {
method addListener (line 29) | addListener(eventName: EventFilter | string, listener: Listener): this {
method removeAllListeners (line 32) | removeAllListeners(eventName: EventFilter | string): this {
method removeListener (line 35) | removeListener(eventName: any, listener: Listener): this {
method advanceTime (line 39) | advanceTime(by: BigNumberish): Promise<ContractTransaction> {
method burn (line 43) | burn(
method collectStatic (line 51) | collectStatic(
method collect (line 66) | collect(
method collectProtocolStatic (line 76) | collectProtocolStatic(
method collectProtocol (line 89) | collectProtocol(
method factory (line 97) | factory(): Promise<string> {
method fee (line 101) | fee(): Promise<number> {
method feeGrowthGlobal0X128 (line 105) | feeGrowthGlobal0X128(): Promise<BigNumber> {
method feeGrowthGlobal1X128 (line 109) | feeGrowthGlobal1X128(): Promise<BigNumber> {
method flash (line 113) | flash(
method increaseObservationCardinalityNext (line 122) | increaseObservationCardinalityNext(
method initialize (line 131) | initialize(sqrtPriceX96: BigNumberish): void {
method liquidity (line 135) | liquidity(): Promise<BigNumber> {
method maxLiquidityPerTick (line 139) | maxLiquidityPerTick(): Promise<BigNumber> {
method mint (line 143) | mint(
method observations (line 153) | observations(arg0: BigNumberish): Promise<{
method observe (line 166) | observe(secondsAgos: BigNumberish[]): Promise<{
method positions (line 175) | positions(arg0: BytesLike): Promise<{
method protocolFees (line 190) | protocolFees(): Promise<{
method setFeeGrowthGlobal0X128 (line 199) | setFeeGrowthGlobal0X128(
method setFeeGrowthGlobal1X128 (line 205) | setFeeGrowthGlobal1X128(
method setFeeProtocol (line 211) | setFeeProtocol(
method slot0 (line 218) | slot0(): Promise<{
method snapshotCumulativesInside (line 237) | snapshotCumulativesInside(
method swap (line 251) | swap(
method tickBitmap (line 261) | tickBitmap(arg0: BigNumberish): Promise<BigNumber> {
method tickSpacing (line 265) | tickSpacing(): Promise<number> {
method ticks (line 269) | ticks(arg0: BigNumberish): Promise<{
method time (line 290) | time(): Promise<BigNumber> {
method token0 (line 294) | token0(): Promise<string> {
method token1 (line 298) | token1(): Promise<string> {
FILE: test/stubs/TestERC20.d.ts
type TestERC20Interface (line 23) | interface TestERC20Interface extends ethers.utils.Interface {
class TestERC20 (line 74) | class TestERC20 extends Contract {
FILE: test/stubs/TestUniswapV3Callee.d.ts
type TestUniswapV3CalleeInterface (line 23) | interface TestUniswapV3CalleeInterface extends ethers.utils.Interface {
class TestUniswapV3Callee (line 140) | class TestUniswapV3Callee extends Contract {
FILE: test/stubs/TestUniswapV3ReentrantCallee.d.ts
type TestUniswapV3ReentrantCalleeInterface (line 23) | interface TestUniswapV3ReentrantCalleeInterface extends ethers.utils.Int...
class TestUniswapV3ReentrantCallee (line 50) | class TestUniswapV3ReentrantCallee extends Contract {
FILE: test/stubs/TestUniswapV3SwapPay.d.ts
type TestUniswapV3SwapPayInterface (line 23) | interface TestUniswapV3SwapPayInterface extends ethers.utils.Interface {
class TestUniswapV3SwapPay (line 55) | class TestUniswapV3SwapPay extends Contract {
FILE: test/stubs/TickTest.ts
class TickTest (line 6) | class TickTest {
method constructor (line 8) | constructor(manager: TickManager) {
method clear (line 11) | clear(tick: number): Promise<void> {
method cross (line 16) | cross(
method getFeeGrowthInside (line 27) | getFeeGrowthInside(
method setTick (line 48) | setTick(
method tickSpacingToMaxLiquidityPerTick (line 68) | tickSpacingToMaxLiquidityPerTick(tickSpacing: number): Promise<JSBI> {
method ticks (line 74) | ticks(tickIndex: number): Promise<MockableTick> {
method update (line 80) | update(
FILE: test/stubs/UniswapV3Factory.d.ts
type UniswapV3FactoryInterface (line 23) | interface UniswapV3FactoryInterface extends ethers.utils.Interface {
class UniswapV3Factory (line 82) | class UniswapV3Factory extends Contract {
Condensed preview — 164 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (807K chars).
[
{
"path": ".github/workflows/lint-from-fork.yml",
"chars": 653,
"preview": "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 "
},
{
"path": ".github/workflows/unit-tests-from-fork.yml",
"chars": 965,
"preview": "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"
},
{
"path": ".gitignore",
"chars": 2246,
"preview": "# 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"
},
{
"path": ".prettierrc.json",
"chars": 3,
"preview": "{}\n"
},
{
"path": "LICENSE",
"chars": 3995,
"preview": "Business Source License 1.1\n\nLicense text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.\n\"Business Sour"
},
{
"path": "README.md",
"chars": 2347,
"preview": "[\n\n- [How \"Tuner\" Library Works?](docs/how-tuner-library-"
},
{
"path": "abi/OHM.json",
"chars": 10568,
"preview": "[\n { \"inputs\": [], \"stateMutability\": \"nonpayable\", \"type\": \"constructor\" },\n {\n \"anonymous\": false,\n \"inputs\": "
},
{
"path": "abi/UniswapV3Pool2.json",
"chars": 21439,
"preview": "[\n {\n \"inputs\": [],\n \"stateMutability\": \"nonpayable\",\n \"type\": \"constructor\"\n },\n {\n \"anonymous\": false,\n"
},
{
"path": "abi/Visor.json",
"chars": 14550,
"preview": "[\n {\n \"inputs\": [\n { \"internalType\": \"address\", \"name\": \"_pool\", \"type\": \"address\" },\n { \"internalType\": \""
},
{
"path": "docs/about-core-pool-config.md",
"chars": 602,
"preview": "### About Core Pool Config\n\nA `PoolConfig` is a group of key meta parameters of an Uniswap V3 Core Pool. In details, `Ti"
},
{
"path": "docs/building-a-client-instance.md",
"chars": 1105,
"preview": "### Building a client instance\n\nA `SimulatorClient` is the user entry point of the Tuner. It needs a `SimulationDataMana"
},
{
"path": "docs/configuration.md",
"chars": 377,
"preview": "### Configuration\n\nWhen Tuner is run, it searches for the closest tuner.config.js file starting from the Current Working"
},
{
"path": "docs/contributing.md",
"chars": 94,
"preview": "### 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",
"chars": 4519,
"preview": "### Fetching all the data of a certain pool from Ethereum\n\nTuner syncs the full state of the Pool from the event logs on"
},
{
"path": "docs/forking-and-retracing.md",
"chars": 1380,
"preview": "### Forking & Retracing\n\nDuring the lifetime of a program, Tuner will record every step that a `ConfigurableCorePool` we"
},
{
"path": "docs/getting-a-core-pool-instance.md",
"chars": 527,
"preview": "### Getting a Core Pool instance\n\nA simple way to get a Core Pool instance is to build a new one according to the `PoolC"
},
{
"path": "docs/getting-a-pool-instance-with-the-data-fetched.md",
"chars": 1667,
"preview": "### Getting a pool instance with the data fetched\n\nUsually before work begins, the preparation includes the following st"
},
{
"path": "docs/how-tuner-library-works.md",
"chars": 1747,
"preview": "### How \"Tuner\" Library Works?\n\nThe overall design of the simulator consists of several components (rough dependency gra"
},
{
"path": "docs/installing-tuner.md",
"chars": 234,
"preview": "### Installing \"Tuner\"\n\n{% embed url=\"https://www.npmjs.com/package/@bella-defintech/uniswap-v3-simulator\" %}\n\n```bash\ny"
},
{
"path": "docs/interacting-with-core-pool.md",
"chars": 1337,
"preview": "### Interacting with Core Pool\n\nA `CorePool` corresponds to a contract of Uniswap V3 Core Pool. You can inspect its stat"
},
{
"path": "docs/loading-and-streaming-events-into-a-pool.md",
"chars": 4531,
"preview": "### Loading and streaming events into a pool\n\nSuppose a quant developer is going to backtest some strategy on mainnet po"
},
{
"path": "docs/performance.md",
"chars": 417,
"preview": "### Performance\n\nEnvironment: AWS EC2 t3.xlarge on us-east-1, RPC provider by Alchemy, Subgraph by hosted service on the"
},
{
"path": "docs/persisting-and-recovering.md",
"chars": 1136,
"preview": "### Persisting & Recovering\n\nIf a state is important enough that you want to test something on it across multiple progra"
},
{
"path": "docs/pool-state-and-transition.md",
"chars": 435,
"preview": "### PoolState & Transition\n\nA `ConfigurableCorePool` is a state machine based on the math model aka `CorePool` of Uniswa"
},
{
"path": "docs/post-processor.md",
"chars": 1382,
"preview": "### Post-processor\n\nA post-processor registered in `ConfigurableCorePool` works as an interceptor to execute callback fu"
},
{
"path": "docs/quick-start.md",
"chars": 1676,
"preview": "### Quick Start\n\n```typescript\nimport {\n SimulationDataManager,\n SimulatorClient,\n SQLiteSimulationDataManager,\n} fro"
},
{
"path": "docs/simulator-roadmap-manager.md",
"chars": 1458,
"preview": "### SimulatorRoadmapManager\n\nIf we take every `ConfigurableCorePool` as a state machine, then a `SimulatorRoadmapManager"
},
{
"path": "examples/Uniswap-v3-Events-Downloader/EventsDownloader.ts",
"chars": 1728,
"preview": "import { EndBlockTypeWhenInit, BSCDataDownloader } from \"../../src\";\nimport { EventDataSourceType } from \"../../src/enum"
},
{
"path": "examples/Uniswap-v3-Events-Downloader/EventsUpdater.ts",
"chars": 2085,
"preview": "import {\n EndBlockTypeWhenRecover,\n BSCDataDownloader,\n EventDBManager,\n} from \"../../src\";\nimport { EventDataSourceT"
},
{
"path": "examples/Uniswap-v3-Strategy-Backtest/README.md",
"chars": 223,
"preview": "# Bella-DeFinTech / uniswap-v3-bot\n\n- We have made a demo as a platform to build and backtest strategy based on the Tune"
},
{
"path": "hardhat.config.ts",
"chars": 962,
"preview": "import \"@typechain/hardhat\";\nimport \"@nomiclabs/hardhat-ethers\";\nimport \"@nomiclabs/hardhat-waffle\";\n\nexport default {\n "
},
{
"path": "package.json",
"chars": 2183,
"preview": "{\n \"name\": \"@bella-defintech/uniswap-v3-simulator\",\n \"version\": \"0.1.9\",\n \"description\": \"the 'Tuner', a Uniswap V3 S"
},
{
"path": "src/client/BSCDataDownloader.ts",
"chars": 44617,
"preview": "import { EventType } from \"../enum/EventType\";\nimport { EventDBManager } from \"../manager/EventDBManager\";\nimport { BigN"
},
{
"path": "src/client/SimulatorClient.ts",
"chars": 6468,
"preview": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { PoolState } from \"../model/PoolState\";\nimp"
},
{
"path": "src/client/index.ts",
"chars": 72,
"preview": "export * from \"./SimulatorClient\";\nexport * from \"./BSCDataDownloader\";\n"
},
{
"path": "src/config/TunerConfig.ts",
"chars": 1300,
"preview": "import path from \"path\";\nimport * as fs from \"fs\";\n\nexport interface TunerConfig {\n RPCProviderUrl: string;\n}\n\nexport f"
},
{
"path": "src/core/ConfigurableCorePool.ts",
"chars": 21068,
"preview": "import JSBI from \"jsbi\";\nimport { PoolState } from \"../model/PoolState\";\nimport { Record } from \"../entity/Record\";\nimpo"
},
{
"path": "src/core/CorePool.ts",
"chars": 25579,
"preview": "import JSBI from \"jsbi\";\nimport assert from \"assert\";\nimport { TickManager } from \"../manager/TickManager\";\nimport { Pos"
},
{
"path": "src/core/index.ts",
"chars": 28,
"preview": "export * from \"./CorePool\";\n"
},
{
"path": "src/entity/EndBlockType.ts",
"chars": 250,
"preview": "export type EndBlockTypeWhenInit =\n | number\n | \"latest\"\n | \"afterDeployment\"\n | \"afterInitialization\";\n\nexport type"
},
{
"path": "src/entity/LiquidityEvent.ts",
"chars": 406,
"preview": "import { EventType } from \"../enum/EventType\";\nimport JSBI from \"jsbi\";\n\nexport interface LiquidityEvent {\n id: number;"
},
{
"path": "src/entity/Record.ts",
"chars": 506,
"preview": "import { ActionType } from \"../enum/ActionType\";\nimport {\n BurnParams,\n CollectParams,\n ForkParams,\n GeneralReturnPa"
},
{
"path": "src/entity/Snapshot.ts",
"chars": 536,
"preview": "import JSBI from \"jsbi\";\nimport { PositionManager } from \"../manager/PositionManager\";\nimport { TickManager } from \"../m"
},
{
"path": "src/entity/SnapshotProfile.ts",
"chars": 72,
"preview": "export type SnapshotProfile = {\n id: string;\n description: string;\n};\n"
},
{
"path": "src/entity/StepComputations.ts",
"chars": 215,
"preview": "import JSBI from \"jsbi\";\n\nexport type StepComputations = {\n sqrtPriceStartX96: JSBI;\n tickNext: number;\n initialized:"
},
{
"path": "src/entity/SwapEvent.ts",
"chars": 405,
"preview": "import { EventType } from \"../enum/EventType\";\nimport JSBI from \"jsbi\";\n\nexport interface SwapEvent {\n id: number;\n ty"
},
{
"path": "src/entity/index.ts",
"chars": 121,
"preview": "export * from \"./Record\";\nexport * from \"./Snapshot\";\nexport * from \"./SnapshotProfile\";\nexport * from \"./EndBlockType\";"
},
{
"path": "src/enum/ActionType.ts",
"chars": 147,
"preview": "export enum ActionType {\n INITIALIZE = \"initialize\",\n MINT = \"mint\",\n BURN = \"burn\",\n COLLECT = \"collect\",\n SWAP = "
},
{
"path": "src/enum/EventDataSourceType.ts",
"chars": 63,
"preview": "export enum EventDataSourceType {\n SUBGRAPH = 1,\n RPC = 2,\n}\n"
},
{
"path": "src/enum/EventType.ts",
"chars": 62,
"preview": "export enum EventType {\n MINT = 1,\n BURN = 2,\n SWAP = 3,\n}\n"
},
{
"path": "src/enum/FeeAmount.ts",
"chars": 178,
"preview": "/**\n * The default factory enabled fee amounts, denominated in hundredths of bips.\n */\nexport enum FeeAmount {\n EXTRA_L"
},
{
"path": "src/enum/InternalConstants.ts",
"chars": 1923,
"preview": "import JSBI from \"jsbi\";\nimport { FeeAmount } from \"./FeeAmount\";\n\nimport dotenv from \"dotenv\";\n\n// load .env file\ndoten"
},
{
"path": "src/enum/index.ts",
"chars": 127,
"preview": "export * from \"./ActionType\";\nexport * from \"./FeeAmount\";\nexport * from \"./EventType\";\nexport * from \"./EventDataSource"
},
{
"path": "src/index.ts",
"chars": 205,
"preview": "export * from \"./client\";\nexport * from \"./core\";\nexport * from \"./manager\";\nexport * from \"./entity\";\nexport * from \"./"
},
{
"path": "src/interface/ActionParams.ts",
"chars": 1117,
"preview": "import JSBI from \"jsbi\";\nimport { ActionType } from \"../enum/ActionType\";\n\nexport type MethodParams =\n | InitializePara"
},
{
"path": "src/interface/ConfigurableCorePool.ts",
"chars": 3467,
"preview": "import JSBI from \"jsbi\";\nimport { SwapEvent } from \"../entity/SwapEvent\";\nimport { CorePoolView } from \"./CorePoolView\";"
},
{
"path": "src/interface/CorePoolView.ts",
"chars": 296,
"preview": "import { CorePool } from \"../core/CorePool\";\n\nexport type CorePoolView = Pick<\n CorePool,\n {\n [K in keyof CorePool]"
},
{
"path": "src/interface/PoolStateContainer.ts",
"chars": 253,
"preview": "import { PoolState } from \"../model/PoolState\";\n\nexport interface PoolStateContainer {\n addPoolState: (PoolState: PoolS"
},
{
"path": "src/interface/PoolStateView.ts",
"chars": 359,
"preview": "import { PoolState } from \"../model/PoolState\";\n\nexport type PoolStateView = Pick<\n PoolState,\n {\n [K in keyof Pool"
},
{
"path": "src/interface/PositionView.ts",
"chars": 188,
"preview": "import { Position } from \"../model/Position\";\n\nexport type PositionView = Pick<\n Position,\n {\n [K in keyof Position"
},
{
"path": "src/interface/SimulationDataManager.ts",
"chars": 752,
"preview": "import { PoolState } from \"../model/PoolState\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { Roadmap } from \""
},
{
"path": "src/interface/SimulatorRoadmapManager.ts",
"chars": 377,
"preview": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\n\nexport interface SimulatorRoadmapManager {\n print"
},
{
"path": "src/interface/SimulatorVisitor.ts",
"chars": 674,
"preview": "import { PoolState } from \"../model/PoolState\";\nimport { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimp"
},
{
"path": "src/interface/TickView.ts",
"chars": 160,
"preview": "import { Tick } from \"../model/Tick\";\n\nexport type TickView = Pick<\n Tick,\n {\n [K in keyof Tick]: Tick[K] extends F"
},
{
"path": "src/interface/Transition.ts",
"chars": 208,
"preview": "import { PoolStateView } from \"./PoolStateView\";\nimport { Record } from \"../entity/Record\";\n\nexport interface Transition"
},
{
"path": "src/interface/Visitable.ts",
"chars": 141,
"preview": "import { SimulatorVisitor } from \"./SimulatorVisitor\";\n\nexport interface Visitable {\n accept(visitor: SimulatorVisitor)"
},
{
"path": "src/interface/index.ts",
"chars": 311,
"preview": "export * from \"./ActionParams\";\nexport * from \"./ConfigurableCorePool\";\nexport * from \"./CorePoolView\";\nexport * from \"."
},
{
"path": "src/manager/EventDBManager.ts",
"chars": 33181,
"preview": "import { Knex, knex as knexBuilder } from \"knex\";\nimport { JSBIDeserializer } from \"../util/Serializer\";\nimport { Liquid"
},
{
"path": "src/manager/PositionManager.ts",
"chars": 3716,
"preview": "import { Position } from \"../model/Position\";\nimport { jsonMapMember, jsonObject } from \"typedjson\";\nimport JSBI from \"j"
},
{
"path": "src/manager/SQLiteSimulationDataManager.ts",
"chars": 12581,
"preview": "import JSBI from \"jsbi\";\nimport { SimulationDataManager } from \"../interface/SimulationDataManager\";\nimport { PoolState "
},
{
"path": "src/manager/SimulatorConsoleVisitor.ts",
"chars": 1507,
"preview": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { SimulatorVisitor } from \"../interface/Simu"
},
{
"path": "src/manager/SimulatorPersistenceVisitor.ts",
"chars": 1187,
"preview": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { SimulatorVisitor } from \"../interface/Simu"
},
{
"path": "src/manager/SimulatorRoadmapManager.ts",
"chars": 3595,
"preview": "import { ConfigurableCorePool } from \"../core/ConfigurableCorePool\";\nimport { Roadmap, toString as printRoadmap } from \""
},
{
"path": "src/manager/TickManager.ts",
"chars": 6247,
"preview": "import JSBI from \"jsbi\";\nimport assert from \"assert\";\nimport { Tick } from \"../model/Tick\";\nimport { jsonMapMember, json"
},
{
"path": "src/manager/index.ts",
"chars": 81,
"preview": "export * from \"./SQLiteSimulationDataManager\";\nexport * from \"./EventDBManager\";\n"
},
{
"path": "src/model/PoolConfig.ts",
"chars": 791,
"preview": "import { FeeAmount } from \"../enum/FeeAmount\";\nimport { IdGenerator } from \"../util/IdGenerator\";\n\nexport class PoolConf"
},
{
"path": "src/model/PoolState.ts",
"chars": 4822,
"preview": "import JSBI from \"jsbi\";\nimport { PoolConfig } from \"./PoolConfig\";\nimport { Transition } from \"../model/Transition\";\nim"
},
{
"path": "src/model/Position.ts",
"chars": 2874,
"preview": "import JSBI from \"jsbi\";\nimport { jsonMember, jsonObject } from \"typedjson\";\nimport { Q128, ZERO } from \"../enum/Interna"
},
{
"path": "src/model/Roadmap.ts",
"chars": 637,
"preview": "import { IdGenerator } from \"../util/IdGenerator\";\n\nexport class Roadmap {\n readonly id: string;\n readonly description"
},
{
"path": "src/model/Tick.ts",
"chars": 4263,
"preview": "import JSBI from \"jsbi\";\nimport assert from \"assert\";\nimport { TickMath } from \"../util/TickMath\";\nimport { jsonMember, "
},
{
"path": "src/model/Transition.ts",
"chars": 1648,
"preview": "import { PoolState } from \"./PoolState\";\nimport { Record } from \"../entity/Record\";\nimport { Visitable } from \"../interf"
},
{
"path": "src/model/index.ts",
"chars": 80,
"preview": "export { PoolConfig } from \"./PoolConfig\";\nexport { Roadmap } from \"./Roadmap\";\n"
},
{
"path": "src/util/BNUtils.ts",
"chars": 1007,
"preview": "import BigNumber from \"bignumber.js\";\nimport { BigNumber as BN } from \"ethers\";\nimport JSBI from \"jsbi\";\n\nBigNumber.conf"
},
{
"path": "src/util/DateConverter.ts",
"chars": 259,
"preview": "import dayjs from \"dayjs\";\n\nexport abstract class DateConverter {\n static parseDate(dateStr: string): Date {\n return"
},
{
"path": "src/util/DateUtils.ts",
"chars": 1373,
"preview": "export function getDate(\n year: number,\n month: number,\n day: number,\n hour: number = 0,\n minute: number = 0,\n sec"
},
{
"path": "src/util/FileUtils.ts",
"chars": 376,
"preview": "import { accessSync, constants } from \"fs\";\nimport { basename } from \"path\";\n\nexport function exists(filePath: string): "
},
{
"path": "src/util/FullMath.ts",
"chars": 2646,
"preview": "import JSBI from \"jsbi\";\nimport { ZERO, ONE, MaxUint256, TWO } from \"../enum/InternalConstants\";\nimport assert from \"ass"
},
{
"path": "src/util/IdGenerator.ts",
"chars": 126,
"preview": "import { v4, validate } from \"uuid\";\n\nexport abstract class IdGenerator {\n static guid = v4;\n static validate = valida"
},
{
"path": "src/util/LiquidityMath.ts",
"chars": 5574,
"preview": "import JSBI from \"jsbi\";\nimport { NEGATIVE_ONE, ZERO, MaxUint128, Q96 } from \"../enum/InternalConstants\";\nimport assert "
},
{
"path": "src/util/PoolStateHelper.ts",
"chars": 3931,
"preview": "import { CorePool } from \"../core/CorePool\";\nimport { Snapshot } from \"../entity/Snapshot\";\nimport { PoolState } from \"."
},
{
"path": "src/util/Serializer.ts",
"chars": 1408,
"preview": "import JSBI from \"jsbi\";\nimport { TypedJSON } from \"typedjson\";\nimport { Constructor } from \"typedjson/src/types\";\nimpor"
},
{
"path": "src/util/SqrtPriceMath.ts",
"chars": 5593,
"preview": "import JSBI from \"jsbi\";\nimport { FullMath } from \"./FullMath\";\nimport {\n ONE,\n ZERO,\n Q96,\n MaxUint160,\n MaxUint25"
},
{
"path": "src/util/SwapMath.ts",
"chars": 4781,
"preview": "import JSBI from \"jsbi\";\nimport { FullMath } from \"./FullMath\";\nimport { SqrtPriceMath } from \"./SqrtPriceMath\";\nimport "
},
{
"path": "src/util/TickMath.ts",
"chars": 6780,
"preview": "import assert from \"assert\";\nimport JSBI from \"jsbi\";\nimport {\n ZERO,\n ONE,\n TWO,\n Q32,\n MaxUint128,\n MaxUint256,\n"
},
{
"path": "src/util/index.ts",
"chars": 237,
"preview": "export * from \"./Serializer\";\nexport * from \"./DateConverter\";\nexport * from \"./FullMath\";\nexport * from \"./TickMath\";\ne"
},
{
"path": "test/ConfigurableCorePool.test.ts",
"chars": 17046,
"preview": "import * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promised\";\nimport { FeeAmount } from \"../src/enum/FeeA"
},
{
"path": "test/JSBI.test.ts",
"chars": 625,
"preview": "import assert from \"assert\";\nimport JSBI from \"jsbi\";\nimport { TWO } from \"../src/enum/InternalConstants\";\n\ndescribe(\"Te"
},
{
"path": "test/LiquidityMath.test.ts",
"chars": 7828,
"preview": "import { expect } from \"chai\";\nimport JSBI from \"jsbi\";\nimport { MaxUint256 } from \"../src/enum/InternalConstants\";\nimpo"
},
{
"path": "test/Serializer.test.ts",
"chars": 664,
"preview": "import assert from \"assert\";\nimport { PositionManager } from \"../src/manager/PositionManager\";\nimport { Position } from "
},
{
"path": "test/SimulationDataManager.test.ts",
"chars": 2995,
"preview": "import { PoolState } from \"../src/model/PoolState\";\nimport { FeeAmount } from \"../src/enum/FeeAmount\";\nimport { ONE } fr"
},
{
"path": "test/SimulatorClient.test.ts",
"chars": 5155,
"preview": "import { SimulatorClient } from \"../src/client/SimulatorClient\";\nimport * as chai from \"chai\";\nimport chaiAsPromised fro"
},
{
"path": "test/SimulatorRoadmapManager.test.ts",
"chars": 4360,
"preview": "import * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promised\";\nimport { FeeAmount } from \"../src/enum/FeeA"
},
{
"path": "test/SwapMath.spec.ts",
"chars": 12894,
"preview": "// import { BigNumber } from \"ethers\";\n// import { ethers } from \"hardhat\";\n// import { SwapMathTest } from \"../typechai"
},
{
"path": "test/TestSubgraph.test.ts",
"chars": 744,
"preview": "import { request, gql } from \"graphql-request\";\nimport * as chai from \"chai\";\nimport chaiAsPromised from \"chai-as-promis"
},
{
"path": "test/Tick.spec.ts",
"chars": 13913,
"preview": "import { TickTest } from \"./stubs/TickTest\";\nimport { expect } from \"./shared/expect\";\nimport {\n FeeAmount,\n getMaxLiq"
},
{
"path": "test/TickManager.test.ts",
"chars": 3175,
"preview": "import { Tick } from \"../src/model/Tick\";\nimport { TickMath } from \"../src/util/TickMath\";\nimport { expect } from \"./sha"
},
{
"path": "test/TickMath.test.ts",
"chars": 2869,
"preview": "import { expect } from \"chai\";\nimport JSBI from \"jsbi\";\nimport { ONE, MaxUint256, TWO } from \"../src/enum/InternalConsta"
},
{
"path": "test/UniswapV3Pool.spec.ts.pending",
"chars": 71226,
"preview": "import { ethers, waffle } from \"hardhat\";\nimport { BigNumber, BigNumberish, constants, Wallet } from \"ethers\";\nimport { "
},
{
"path": "test/UniswapV3Pool.swaps.spec.ts.pending",
"chars": 19313,
"preview": "import { Decimal } from \"decimal.js\";\nimport { BigNumber, BigNumberish, ContractTransaction, Wallet } from \"ethers\";\nimp"
},
{
"path": "test/contracts/CorePool.test.ts",
"chars": 5878,
"preview": "import { ethers, waffle } from \"hardhat\";\nimport { Wallet } from \"ethers\";\nimport type { UniswapV3Factory2, UniswapV3Poo"
},
{
"path": "test/contracts/Ticks.test.ts",
"chars": 3685,
"preview": "import { ethers } from \"hardhat\";\nimport { expect } from \"chai\";\nimport { ConfigurableCorePool } from \"../../src/core/Co"
},
{
"path": "test/contracts/src/contracts/NoDelegateCall.sol",
"chars": 1079,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\n/// @title Prevents delegatecall to a contract\n/// @notice"
},
{
"path": "test/contracts/src/contracts/UniswapV3Factory2.sol",
"chars": 2852,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\nimport './interfaces/IUniswapV3Factory.sol';\n\nimport './Un"
},
{
"path": "test/contracts/src/contracts/UniswapV3Pool2.sol",
"chars": 36555,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\nimport './interfaces/IUniswapV3Pool.sol';\n\nimport './NoDel"
},
{
"path": "test/contracts/src/contracts/UniswapV3PoolDeployer2.sol",
"chars": 1411,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity =0.7.6;\n\nimport './interfaces/IUniswapV3PoolDeployer.sol';\n\nimport "
},
{
"path": "test/contracts/src/contracts/interfaces/IERC20Minimal.sol",
"chars": 3074,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Minimal ERC20 interface for Uniswap\n//"
},
{
"path": "test/contracts/src/contracts/interfaces/IUniswapV3Factory.sol",
"chars": 4040,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title The interface for the Uniswap V3 Facto"
},
{
"path": "test/contracts/src/contracts/interfaces/IUniswapV3Pool.sol",
"chars": 809,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\nimport './pool/IUniswapV3PoolImmutables.sol';\nimp"
},
{
"path": "test/contracts/src/contracts/interfaces/IUniswapV3PoolDeployer.sol",
"chars": 1300,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title An interface for a contract that is ca"
},
{
"path": "test/contracts/src/contracts/interfaces/LICENSE",
"chars": 18091,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "test/contracts/src/contracts/interfaces/callback/IUniswapV3FlashCallback.sol",
"chars": 982,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Callback for IUniswapV3PoolActions#fla"
},
{
"path": "test/contracts/src/contracts/interfaces/callback/IUniswapV3MintCallback.sol",
"chars": 980,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Callback for IUniswapV3PoolActions#min"
},
{
"path": "test/contracts/src/contracts/interfaces/callback/IUniswapV3SwapCallback.sol",
"chars": 1293,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Callback for IUniswapV3PoolActions#swa"
},
{
"path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolActions.sol",
"chars": 6304,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Permissionless pool actions\n/// @notic"
},
{
"path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol",
"chars": 2602,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that is not stored\n/// @not"
},
{
"path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolEvents.sol",
"chars": 5964,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Events emitted by a pool\n/// @notice C"
},
{
"path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol",
"chars": 1811,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that never changes\n/// @not"
},
{
"path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol",
"chars": 1188,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Permissioned pool actions\n/// @notice "
},
{
"path": "test/contracts/src/contracts/interfaces/pool/IUniswapV3PoolState.sol",
"chars": 6510,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Pool state that can change\n/// @notice"
},
{
"path": "test/contracts/src/contracts/libraries/BitMath.sol",
"chars": 2790,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title BitMath\n/// @dev This library provides"
},
{
"path": "test/contracts/src/contracts/libraries/FixedPoint128.sol",
"chars": 311,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.4.0;\n\n/// @title FixedPoint128\n/// @notice A library fo"
},
{
"path": "test/contracts/src/contracts/libraries/FixedPoint96.sol",
"chars": 380,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.4.0;\n\n/// @title FixedPoint96\n/// @notice A library for"
},
{
"path": "test/contracts/src/contracts/libraries/FullMath.sol",
"chars": 5114,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.0;\n\n/// @title Contains 512-bit math functions\n/// @notice Facilit"
},
{
"path": "test/contracts/src/contracts/libraries/LICENSE_GPL",
"chars": 18091,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "test/contracts/src/contracts/libraries/LICENSE_MIT",
"chars": 1056,
"preview": "Copyright (c) 2021 Remco Bloemen\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this s"
},
{
"path": "test/contracts/src/contracts/libraries/LiquidityMath.sol",
"chars": 622,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Math library for liquidity\nlibrary Liq"
},
{
"path": "test/contracts/src/contracts/libraries/LowGasSafeMath.sol",
"chars": 1696,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.7.0;\n\n/// @title Optimized overflow and underflow safe "
},
{
"path": "test/contracts/src/contracts/libraries/Oracle.sol",
"chars": 16408,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\n/// @title Oracle\n/// @notice Provides price and liquidit"
},
{
"path": "test/contracts/src/contracts/libraries/Position.sol",
"chars": 3575,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './FullMath.sol';\nimport './FixedPoint128.sol';\nim"
},
{
"path": "test/contracts/src/contracts/libraries/SafeCast.sol",
"chars": 1048,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Safe casting methods\n/// @notice Conta"
},
{
"path": "test/contracts/src/contracts/libraries/SqrtPriceMath.sol",
"chars": 10774,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './LowGasSafeMath.sol';\nimport './SafeCast.sol';\n\n"
},
{
"path": "test/contracts/src/contracts/libraries/SwapMath.sol",
"chars": 4633,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './FullMath.sol';\nimport './SqrtPriceMath.sol';\n\n/"
},
{
"path": "test/contracts/src/contracts/libraries/Tick.sol",
"chars": 9695,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './LowGasSafeMath.sol';\nimport './SafeCast.sol';\n\n"
},
{
"path": "test/contracts/src/contracts/libraries/TickBitmap.sol",
"chars": 4141,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity >=0.5.0;\n\nimport './BitMath.sol';\nimport \"hardhat/console.sol\";\n\n//"
},
{
"path": "test/contracts/src/contracts/libraries/TickMath.sol",
"chars": 8638,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Math library for computing sqrt prices"
},
{
"path": "test/contracts/src/contracts/libraries/TransferHelper.sol",
"chars": 933,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.6.0;\n\nimport '../interfaces/IERC20Minimal.sol';\n\n/// @t"
},
{
"path": "test/contracts/src/contracts/libraries/UnsafeMath.sol",
"chars": 660,
"preview": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Math functions that do not check input"
},
{
"path": "test/shared/MockableTick.ts",
"chars": 650,
"preview": "import JSBI from \"jsbi\";\nimport { Tick } from \"../../src/model/Tick\";\n\nexport class MockableTick extends Tick {\n // [CA"
},
{
"path": "test/shared/checkObservationEquals.ts",
"chars": 1127,
"preview": "import { BigNumber, BigNumberish } from \"ethers\";\nimport { expect } from \"./expect\";\n\n// helper function because we cann"
},
{
"path": "test/shared/expect.ts",
"chars": 206,
"preview": "import { expect, use } from \"chai\";\nimport { solidity } from \"ethereum-waffle\";\nimport { jestSnapshotPlugin } from \"moch"
},
{
"path": "test/shared/fixtures.ts",
"chars": 3677,
"preview": "import { BigNumber } from \"ethers\";\nimport { ethers } from \"hardhat\";\nimport { MockTimeUniswapV3Pool } from \"../../typec"
},
{
"path": "test/shared/format.ts",
"chars": 414,
"preview": "import { Decimal } from \"decimal.js\";\nimport { BigNumberish } from \"ethers\";\n\nexport function formatTokenAmount(num: Big"
},
{
"path": "test/shared/snapshotGasCost.ts",
"chars": 925,
"preview": "import {\n TransactionReceipt,\n TransactionResponse,\n} from \"@ethersproject/abstract-provider\";\nimport { expect } from "
},
{
"path": "test/shared/utilities.ts",
"chars": 9262,
"preview": "import bn from \"bignumber.js\";\n// import {\n// BigNumber,\n// BigNumberish,\n// constants,\n// Contract,\n// Contra"
},
{
"path": "test/stubs/MockTimeUniswapV3Pool.ts",
"chars": 6928,
"preview": "import { EventFilter, BigNumber, BigNumberish, Signer } from \"ethers\";\nimport { ContractTransaction } from \"@ethersproje"
},
{
"path": "test/stubs/TestERC20.d.ts",
"chars": 10993,
"preview": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n ethers,\n EventFil"
},
{
"path": "test/stubs/TestUniswapV3Callee.d.ts",
"chars": 27512,
"preview": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n ethers,\n EventFil"
},
{
"path": "test/stubs/TestUniswapV3ReentrantCallee.d.ts",
"chars": 4592,
"preview": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n ethers,\n EventFil"
},
{
"path": "test/stubs/TestUniswapV3SwapPay.d.ts",
"chars": 6586,
"preview": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n ethers,\n EventFil"
},
{
"path": "test/stubs/TickTest.ts",
"chars": 2460,
"preview": "import { TickManager } from \"../../src/manager/TickManager\";\nimport { TickMath } from \"../../src/util/TickMath\";\nimport "
},
{
"path": "test/stubs/UniswapV3Factory.d.ts",
"chars": 12862,
"preview": "/* Autogenerated file. Do not edit manually. */\n/* tslint:disable */\n/* eslint-disable */\n\nimport {\n ethers,\n EventFil"
},
{
"path": "tsconfig.json",
"chars": 463,
"preview": "{\n \"compilerOptions\": {\n \"experimentalDecorators\": true,\n \"target\": \"es2018\",\n \"module\": \"commonjs\",\n \"stri"
},
{
"path": "tuner.config.js",
"chars": 74,
"preview": "module.exports = {\n RPCProviderUrl: process.env.MAINNET_PROVIDER_URL,\n};\n"
}
]
About this extraction
This page contains the full source code of the Bella-DeFinTech/uniswap-v3-simulator GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 164 files (745.0 KB), approximately 192.6k tokens, and a symbol index with 481 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.