UniswapV3Pool#
Uniswap V3 Usage#
Warning
TWAP Oracle support is currently experimental, and not suggested for use. Additionally, fee switch behavior is not yet tested, and any protocol_fees may not be implemented correctly.
Creating Uniswap V3 Pools#
from nethermind.entro.base import PoolFactory
factory = PoolFactory(w3='https://', sqlalchemy_url='postgresql:db_uri')
# Create an empty pool with no liquidity & 1:1 price ratio, and 0.3% fee
low_fee = factory.initialize_empty_pool("uniswap_v3")
# Create a pool with liquidity & 1:1 price ratio, and 1% fee
high_fee = factory.initialize_empty_pool("uniswap_v3", initialization_args={"fee":10_000, "tick_spacing":200})
Initializing Pools From Chain#
Pool Initialization Sequence Pool initialization from chain is fairly taxing, requiring the following steps:
Fetching the pool’s immutable parameters & state (1-3 seconds and ~20 RPC Calls)
- Fetching the Initialized Ticks:
Search Tick Bitmap (~120 queries on pool with 60 tick spacing. ~700 on pool with 10 tick spacing).
# Load the current USDC/WETH .3% pool from mainnet
usdc_weth = factory.initialize_from_chain(
"uniswap_v3",
pool_address="0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
)
# Load historical USDC/WETH .3% pool from mainnet
usdc_weth = factory.initialize_from_chain(
"uniswap_v3",
pool_address="0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
at_block=14_000_000,
)
Warning
When using initialize_from_chain with a non-archival node, the chain state is typically only stored for the last 50 blocks. If a pool is initialized without a block identifier, the current block will be fetched, and the pool will be initialized at that block. On a slow RPC and a busy pool, it may take upwards of 20 minutes to initialize a pool, and the state may be discarded, leaving the pool in an uninitialized state.
API Documentation#
- class nethermind.entro.uniswap_v3.UniswapV3Pool#
Class to simulate behavior of Uniswap V3 pools in python. Reproduces solidity integer rounding behavior when exact math mode enabled.
Re-Implements all functions of UniswapV3 pools in python, and provides research functionality.
- math#
alias of
UniswapV3Math
- advance_block(blocks: int = 1)#
Advances the current block by blocks and adds 12 seconds to the block_timestamp for each block progressed :param blocks: Number of blocks to advance forward. Defaults to 1
- burn(owner_address: ChecksumAddress, tick_lower: int, tick_upper: int, amount: int, committing: bool = True) tuple[int, int] #
- Parameters:
owner_address – Owner of the liquidity to burn.
tick_lower – Lower bound of the liquidity to burn.
tick_upper – Upper bound of the liquidity to burn.
amount – Amount of liquidity to burn.
committing – If True, all computed values are saved into the pool state. If False, only computes the token_0 and token_1 value of the position without deactivating or modifying the fee accrual of the position Default: True
- Returns:
- decode_price_from_event(*args, **kwargs)#
Decodes the price from an event
- Parameters:
event –
reference_token –
- Returns:
- classmethod enable_exact_math()#
Enables exact math mode for all pools. Exact math mode is slower, but more closely matches the on-chain behavior of Uniswap V3 pools. Exact math mode is disabled by default.
- classmethod get_abi(json_string: bool = False) dict[str, Any] | str #
Returns the ABI for a UniswapV3Pool contract
- Parameters:
json_string – If True, returns the ABI as a JSON string. Otherwise, returns a dictionary
- get_price_at_sqrt_ratio(sqrt_price: int, reverse_tokens: bool = False) float #
Converts a sqrt_price to a human-readable price.
- Parameters:
sqrt_price – sqrt_price encoded as fixed point Q64.96
reverse_tokens – Whether to reverse the tokens in the price. The sqrt_price represents the \(\frac{ Token 1 }{ Token 0}\). If reverse_tokens is True, the price will be represented as \(\frac{ Token 0 }{ Token 1}\).
- classmethod load_pool(file_path, w3: Web3)#
Loads a pool from a JSON File generated by the save_state() method :param file_path: Filepath of JSON File :param w3: Web3 instance :return:
- mint(recipient: ChecksumAddress, tick_lower: int, tick_upper: int, amount: int)#
- save_pool(file_path)#
Saves pool state & parameters to JSON file. This file can later be used to re-initialize a pool instance without requiring large numbers of on-chain queries.
- Parameters:
file_path – Filepath for JSON save location
- swap(zero_for_one: bool, amount_specified: int, sqrt_price_limit: int, log_swap: bool = False, db_session: Session | None = None) str | None #
Swaps tokens in the pool.
- Parameters:
zero_for_one – Which direction to swap tokens. If True, sell Token 0 and buy Token 1. If False, sell Token 1 and buy Token 0.
amount_specified – Raw token amount to swap. If amount specified is positive, this represents the quantity of tokens to sell. If amount specified is negative, this represents the quantity of tokens to buy.
sqrt_price_limit – The maximum/minimum price to allow for the swap. If the swap would result in a price above this limit, the swap will only be executed up to the price sqrt_price_limit. If the sqrt_price_limit is out of range, and no tokens can be swapped, a UniswapV3Revert exception will be raised.
log_swap – If True, logs the swap to the database & returns a swap_id that can be used to query the swap data. If False, does not log the swap to the database & returns None. Default: False
- block_number: int#
Current block number of the pool. Is used when logging analytics, and for replaying historical swaps. Can be manually incremented through the advance_block() method.
- block_timestamp: int#
Current Block timestamp of the pool. Is required to tracking oracle observations, and tracking liquidity position fee growth. Every time advance_block() is called, this value is incremented by 12 seconds.
- immutables: PoolImmutables#
PoolImmutables object containing the pool fee, token_0, token_1, and other immutable parameters
- init_mode: None | Literal['exact_simulation', 'simulation', 'load_liquidity', 'chain_translation'] = None#
Parameter to describe what data to load from chain. If None, pool is not initialized from chain, and is a testing pool instead.
- observations: list[nethermind.entro.types.uniswap_v3.OracleObservation]#
List of all oracle observations. Oracle observations are not yet fully supported, so this API will likely change
- pool_contract: Contract | None = None#
web3.contract.Contract
object for interacting with the on-chain pool. Is only present when pool is initialized through the from_chain() method.
- positions: dict[tuple[eth_typing.evm.ChecksumAddress, int, int], nethermind.entro.types.uniswap_v3.PositionInfo]#
Dictionary of all LP positions. The key to access the info for a position is a tuple of the owner address, lower tick, and upper tick of the position.
For on-chain uniswap V3 pools, the majority of all liquidity is owned by the UniswapV3NFTPositionManager that is deployed at 0xC364…FE88
- slot0: Slot0#
Slot0 object containing all current state of the pool. Used to track the current sqrt_prce and tick of the pool. Also tracks data for accumulating oracle
- state: PoolState#
PoolState object containing all current state of the pool. Used to track the current liquidity, balances, and fees
- ticks: dict[int, nethermind.entro.types.uniswap_v3.Tick]#
Dictionary of all initialized Ticks. Ticks are initialized (added to this dictionary) when liquidity is added to a tick, and are deleted from the dictionary when all the liquidity is removed from a tick. This dictionary is unsorted, and when it become necessary to cross a tick, the dictionary keys are sorted and searched. This removes the need for the TickBitmap that is used in the on-chain implementation.
Uniswap V3 Types#
- class nethermind.entro.types.uniswap_v3.PoolImmutables#
Stores the pool’s immutable parameters that are set once during pool initialization and will never change.
- fee: int#
The fee that is charged on each swap. This value is measured in hundredths of a bip (0.0001%).
A pool with a 0.3% swap fee would have an immutable fee value of 3000, and a .05% pool would have an immutable fee parameter of 500
- initialization_block: int | None#
Block number that the pool was initialized. This value is set to None if the pool has not been initialized from a chain
- max_liquidity_per_tick: int#
Parameter that is used to limit the amount of liquidity that can be deployed to a single tick. This parameter is initialized based on the total amount of liquidity that can ever be active without causing overflows, and divides that by the number of usable ticks within the pool
- pool_address: ChecksumAddress#
Deployment address of the pool contract.
- tick_spacing: int#
Number of ticks between liquidity deployments. On a pool with a 0.3% swap fee, the tick spacing is 60 (.6% minimum liquidity ranges) Lower tick spacings increase precision for liquidity providers, but each tick crossing requires several SSLOAD and SSTORE operations, increasing the cost of swaps. On low fee pools with stable assets, tick spacings are low since prices dont fluctuate frequently, and the tick spacing on a 1% pool is 200 (2% minimum liquidity ranges) to reduce the number of ticks that need to be crossed when swapping a volatile asset.
- token_0: ERC20Token#
The Token 0 of the pool that can be bought and sold through this pool
- token_1: ERC20Token#
The Token 1 of the pool that can be bought and sold through this pool
- class nethermind.entro.types.uniswap_v3.PoolState#
Stores the current balances, liquidity, and fee growths
- balance_0: int#
Current balance of token_0 in pool. This value is modified during each swap, and liquidity modification.
- balance_1: int#
Current balance of token_1 in pool. This value is modified during each swap, and liquidity modification.
- fee_growth_global_0: int#
Tracks fee accumulation for token_0. When selling token_0, the swap fee is collected in token_0, and addded to this accumulator. When a position is modified or burned, the position fees are calculated from this global accumulator.
- class nethermind.entro.types.uniswap_v3.Slot0#
Stores the current price, tick, and oracle info
- fee_protocol: int#
Amount of swap fees that are given to the Uniswap Protocol. This is currently turned off on all official Uniswap deployments, but can be turned on by governance at a later date.
- observation_cardinality: int#
Current number of observations stored in the oracle. When this value is increased, the oracle stores more price history within the oracle.
- observation_cardinality_next: int#
Parameter that is used when increasing the historical prices within the oracle.
- class nethermind.entro.types.uniswap_v3.Tick#
Stores liquidity data and fee growth for each tick
- fee_growth_outside_0: int#
Internal parameter tracking fee growth per unit of liquidity when the liquidity in this tick is inactive
- fee_growth_outside_1: int#
Internal parameter tracking the fee growth that occurs when this tick is not active. Used to calculate fees and position valuations
- liquidity_gross: int#
Total liquidity owned by all positions that use this tick as an upper tick or a lower tick. used by the pool to determine if it is okay to delete a tick when a position is removed.
- liquidity_net: int#
Net liquidity to add/remove from the pool when a swap moves the price across tick boundaries. If price is moving up, add liquidity_net to current liquidity. If price is moving down, liquidity_net is subtracted from current liquidity.
- class nethermind.entro.types.uniswap_v3.OracleObservation#
Stores oracle observation data
- class nethermind.entro.types.uniswap_v3.PositionInfo#
Stores the current liquidity, fee growth, and fees owed for each position.
Note
This data is semi-stateful, and doesnt represent the value of a position at a given time. In order to compute the token_0 and token_1 value of a position at a given time, the best mechanism through this library is the save_position_snapshot() function.
- fee_growth_inside_0_last: int#
Token 0 fee growth per unit of ticks since last ticks update. This paramter is allowed to underflow in the Uniswap Codebase, creating bizzare behavior. If the value of this field is a number greater than 10 ** 77, the value has underflowed. This is expected behavior, and is handled by the library.
- fee_growth_inside_1_last: int#
Token 1 Fee growth per unit of ticks since last ticks update. This paramter is allowed to underflow in the same manner as fee_growth_inside_0_last