UniswapV3Pool

Contents

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:

  1. Fetching the pool’s immutable parameters & state (1-3 seconds and ~20 RPC Calls)

  2. Fetching the Initialized Ticks:
    1. 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)#
Parameters:
  • recipient (ChecksumAddress) – Owner address of the minted liquidity.

  • tick_lower (int) – Lower bound of the minted liquidity.

  • tick_upper (int) – Upper bound of the minted liquidity.

  • amount (int) – Amount of liquidity to mint.

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

protocol_fee_0: int = 0#

Amount of Token 0 Owed to the protocol if fee switch is turned on

protocol_fee_1: int = 0#

Amount of Token 1 Owed to the protocol if fee switch is turned on

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.

fee_growth_global_1: int#

Tracks fee accumulation for token_1. When selling token_1, the swap fee is collected in token_1.

liquidity: int#

Amount of active liquidity. This parameter remains constant during swaps where the price does not move ticks. When crossing a tick, this value is increased or reduced.

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.

observation_index: int#

Current index that is used internally to store the oracle data.

sqrt_price: int#

Current Exchange rate between token_0 and token_1.

This value is represented as the square root of the ratio between token_0 and token_1, in a fixed point Q64.96 Number (64 bits of integer precision & 96 bits of fractional precision).

tick: int#

Current Tick of the Pool. Ticks are discrete price ranges representing a 0.1% change in price.

The Token 0 to Token 1 exchange rate at a tick can be calculated by the following formula: 1.0001 ** tick

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.

seconds_outside: int#

Internal fee accrual tracking parameter

seconds_per_liquidity_outside: int#

Internal parameter that tracks the seconds per liquidity outside of this tick. Used to calculate fees

tick_cumulative_outside: int#

the cumulative tick value on the other side of the tick

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

liquidity: int#

Amount of liquidity owned by this position

tokens_owed_0: int#

Number of token_0 owed to the position owner. Is only updated when position is burned

tokens_owed_1: int#

Number of token_1 owed to the position owner. Is only updated when position is burned