transaction_builder.protocols.uniswap_v3.uniswap_v3_sdk
glob
json
math
os
time
Dict
List
Optional
Tuple
get_address_by_chain_and_network
Chain
Network
ISDK
Config
get_blocknative_tip
get_web3_by_network_and_chain
Contract
DEBUG
UniswapV3SDK Objects
class UniswapV3SDK(ISDK)
A Uniswap SDK containing the functions that will be called on the uniswap protocol. NOTE: Common error codes from uniswap contract: https://docs.uniswap.org/contracts/v3/reference/error-codes
UNISWAP_TICK_SPACING
MAX_UINT_128
MAX_UINT_256
UNISWAP_MIN_TICK
UNISWAP_MAX_TICK
Q96
Q128
DEADLINE_100_DAYS
__init__
def __init__(network: Network, chain: Chain)
factory_contract
@property
def factory_contract() -> Contract
router_contract
@property
def router_contract() -> Contract
position_manager_contract
@property
def position_manager_contract() -> Contract
quoter_contract
@property
def quoter_contract() -> Contract
transfer_eth
def transfer_eth(from_address: str,
to_address: str,
amount: int,
nonce_counter: int = 0,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
Transfer ETH from one address to another.
Parameters
from_address: str the address to transfer from to_address: str the address to transfer to amount: int the amount of ETH to transfer
Returns
tx_hash: str the transaction hash
wrap
def wrap(from_address: str,
amount: int,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
Wrap ETH to WETH
Parameters
amount: int the amount of ETH to wrap
Returns
tx_hash: str the transaction hash
unwrap
def unwrap(token_address: str,
from_address: str,
amount: int,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
Unwrap WETH to ETH or other token
Parameters
amount: int the amount of wrap token to unwrap
Returns
tx_hash: str the transaction hash
approve
def approve(token_address: str,
spender_address: str,
from_address: str,
amount: Optional[int] = None,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
Approve a spender to spend a certain amount of tokens on behalf of the user.
Parameters
token_contract: Contract the address of the token to approve spender_contract: Contract the address of the spender from_address: str the address of the user amount: int the amount of tokens to approve. If not provided, defaults to the maximum uint256 value
Returns
tx_hash: str the transaction hash
swap_in
def swap_in(tokenIn: str,
tokenOut: str,
fee: int,
recipient: str,
amountIn: int,
amountOutMinimum: int,
sqrtPriceLimitX96: int,
from_address: str,
transfer_eth_in: bool,
slippage: Optional[float] = None,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None)
Builds an unsigned transaction for a single-path swap on Uniswap V3 using the exactInputSingle function.
Arguments:
tokenInstr - The address of the input token.tokenOutstr - The address of the output token.feeint - The fee tier of the pool, e.g., 3000 for 0.3%.recipientstr - The address to receive the output tokens.amountInint - The amount of input tokens to swap, in the smallest indivisible unit.amountOutMinimumint - The minimum amount of output tokens to receive, in the smallest indivisible unit. this field is not used right now.sqrtPriceLimitX96int - The maximum or minimum sqrt price limit, depending on the swap direction.from_addressstr - The address to send the transaction from.transfer_eth_inbool - Whether to transfer ETH as the input token (if tokenIn is WETH).
Returns:
dict- The unsigned transaction dictionary.
Notes:
- The transaction fees are calculated using the
tokenOut0 method. - The gas limit is estimated and increased by the
tokenOut1 factor. - If
transfer_eth_inis True andtokenInis WETH, the transaction value is set toamountIn. - The transaction type is set to 2 (EIP-1559).
- The nonce is set to the current transaction count for the
from_address.
quoteExactInputSingle
def quoteExactInputSingle(token0_address: str,
token1_address: str,
fee: int,
amount: int,
sqrtPriceLimitX96: int = 0,
block_identifier: Optional[int] = None) -> int
A function to call the quoteExactInputSingle function from the uniswap v3
uni_quoter V2 smart contract.
https://docs.uniswap.org/contracts/v3/reference/periphery/lens/Quoter
Gives the price one would obtain (without slippage). Includes price impact and fees in calculation
Parameters
token0_address: str the address of the input token token1_address: str the address of the output token fee: int the pool fee where the swap will occur amount: int the desired input amount
Returns
quote: int the price in units of the second token. I.e, "quote / 10**token1_decimals" is in nominal units
quoteExactInputSingleV1
def quoteExactInputSingleV1(token0_address: str,
token1_address: str,
fee: int,
amount: int,
sqrtPriceLimitX96: int = 0,
block_identifier: Optional[int] = None) -> int
" Quoter function on the uniswap V1 Quoter contract
quoteExactOutputSingle
def quoteExactOutputSingle(token0_address: str,
token1_address: str,
fee: int,
amount: int,
sqrtPriceLimitX96: int = 0,
block_identifier: Optional[int] = None) -> int
A function to call the quoteExactOutputSingle function from the uniswap v3
uni_quoter smart contract.
https://docs.uniswap.org/contracts/v3/reference/periphery/lens/Quoter
Given the amount you want to get out, produces a quote for the amount in for a swap over a single pool
Parameters
token0_address: str the address of the input token token1_address: str the address of the output token fee: int the pool fee where the swap will occur amount: int the desired output amount
Returns
quote: int The amount required as the input for the swap in order to receive amountOut
quoteExactOutputSingleV1
def quoteExactOutputSingleV1(token0_address: str,
token1_address: str,
fee: int,
amount: int,
sqrtPriceLimitX96: int = 0,
block_identifier: Optional[int] = None) -> int
open_lp_position
def open_lp_position(token0_address: str,
token1_address: str,
fee: int,
lower_bound_price: float,
upper_bound_price: float,
token0_desired: int,
token1_desired: int,
agent_address: str,
amount0_min: int = 0,
amount1_min: int = 0,
slippage: Optional[float] = None,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
Open an lp position on uniswap v3.
Ordering of the pool matters. E.g., if token0_address=WETH, but the pool address token0 = DAI, the amounts and tick ranges are inverted within this function.
NOTE: DOES NOT WORK WITH ETH. Do not send ETH in.
https://docs.uniswap.org/contracts/v3/guides/providing-liquidity/mint-a-position
Arguments:
token0_address (str): token 0 address token1_address (str): token 1 address fee (int): pool fee (e.g. 3000 = 0.3%) lower_bound_price (float): the lower bound of the price to add liquidity in. Note price is in nominal units, e.g., 1WETH = 1500.001 USDT upper_bound_price (float): the upper bound of the price to add liquidity in Note price is in nominal units, e.g., 1WETH = 1500.001 USDT token0_desired (int): the amount of token0 to add into the position in units of token0 Eg., 1ETH is 1018 WEI, so token0 = 1018 for 1 ETH. token1_desired (int): the amount of token1 to add into the position agent_address (str): address of the agent
amount0_minint, optional - description. Defaults to 0.amount1_minint, optional - description. Defaults to 0.slippage- A number between 0 and 1 which determines amountmin = (1-slippage)*desired
Returns:
str- transaction hash of the transaction
close_lp_position
def close_lp_position(agent_address: str,
position_id: int,
amount0_min: Optional[int] = None,
amount1_min: Optional[int] = None,
slippage: Optional[float] = None,
pool_address: Optional[str] = None,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> List[str]
This function removes the liquidity from position_id, collects the fees and tokens from the removed liquidity position_id, and burns position_id.
Arguments:
agent_address (str): The address of the agent position position_id (int): the integer value of the liquidity position amount0_min (int, optional): amount0 of token0 to get out for slippage protection. Defaults to 0. amount1_min (int, optional): amount1 of token1 to get our for slippage protection. Defaults to 0.
slippage- (float, optional): A number between 0 and 1. Defaults to None. (1-slippage)*desired will override min amounts. pool_address (str, optional): The address of the pool, needed only for Slippage calculation. Defaults to None. If slippage isn't provided, this is not needed.
Returns:
-
List[str]- [remove_tx_hash, collect_tx_hash, burn_tx_hash] transaction hashes of the different transactionsNOTE: positions is an ERC721 token:
close_lp_position_multicall
def close_lp_position_multicall(agent_address: str,
position_id: int,
amount0_min: Optional[int] = None,
amount1_min: Optional[int] = None,
slippage: Optional[float] = None,
pool_address: Optional[str] = None,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
remove_lp_liquidity
def remove_lp_liquidity(agent_address: str,
position_id: int,
amount0_min: Optional[int] = None,
amount1_min: Optional[int] = None,
slippage: Optional[float] = None,
pool_address: Optional[str] = None,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
Removes ALL liquidity from the position_id of agent_address. Partial liquidity removal is NOT implemented.
NOTE: Does not move tokens to agents wallet. To do that, call collect(). This function only decreases liquidity position on uniswapv3 internal bookkeeping.
Arguments:
agent_address (str): The address of the agent of the liquidity position position_id (int): The id of the liquidity position to be removed amount0_min (int, optional): Defaults to 0. amount1_min (int, optional): Defaults to 0. liquidity (int): The amount of liquidity to be removed.
Returns:
str: The transaction hash of the position
collect_lp
def collect_lp(agent_address: str,
position_id: int,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
After having accumulated fees, or having decreased a liquidity position, the tokens are still in uniswapv3 position and need to be moved to agents wallet. This function moves/collects the maximum amount of both fees and liquidity to the agents wallet from uniswap contract.
Arguments:
agent_addressstr - agents addressposition_idint - id of the agents position which the own.
Returns:
str- transaction hash of the transaction.
burn_lp_position
def burn_lp_position(agent_address: str,
position_id: int,
set_gas_override: Optional[int] = None,
block_identifier: Optional[int] = None) -> str
Burn the liquidity position of agent_address at position_id NOTE: can't burn until all liquidity is removed and fees collected
Arguments:
agent_address (str): address of agent of the position position_id (int): id of the position of the agent_address
Returns:
str: transaction hash of the burn transaction
get_unclaimed_fees
def get_unclaimed_fees(
pool_address: str,
position_id: int,
block_identifier: Optional[int] = None) -> Tuple[int, int]
For a uniswapv3 liquidity position, find the amount of uncollected fees that exists in a certain position. Returns the unclaimed fees in units of base token (e.g., WEI for WETH).
Arguments:
pool_addressstr - address of the uniswap v3 poolposition_idint - position of the agents liquidity.
Returns:
Tuple[int, int]:
uncollected_fees0- the amount of uncollected fees the agent has in the position in token0 units, e.g., WEI units for WETHuncollected_fees1- the amount of uncollected fees the agent has in the position in token1 units e.g., WEI units for WETH NOTE:https://ethereum.stackexchange.com/questions/101955/trying-to-make-sense-of-uniswap-v3-fees-feegrowthinside0lastx128-feegrowthglobhttps://github.com/someben/pyuv3/blob/main/pyuv3/flowint.pyCheckout the relevant formulas below which are from Uniswap Whitepaper Section 6.3 and 6.4𝑓𝑟 =𝑓𝑔−𝑓𝑏(𝑖𝑙)−𝑓𝑎(𝑖𝑢)
𝑓𝑢 =𝑙·(𝑓𝑟(𝑡1)−𝑓𝑟(𝑡0))
get_lp_token_amounts
def get_lp_token_amounts(
pool_address: str,
position_id: int,
block_identifier: Optional[int] = None) -> Tuple[int, int]
Find the number of tokens inside a single liquidity position for an agent.
Arguments:
pool_address (str): The address of the specific pool of interest position_id (int): the id of the lp position of the agent
Returns:
Tuple[int, int]: amount0 - the number of tokens0 in the position amount1 - the number of tokens1 in the position
NOTE- amounts are in base units, e.g., WEI for WETH NOTE: The logic for getting the token amounts inside a specific tick bin is See 3.3.3 of LIQUIDITY MATH IN UNISWAP V3 by Atis Elsts
calculate_token0_amount
def calculate_token0_amount(liquidity, sp, sa, sb)
Calculate the number of tokens0 inside an individual tick bin
Arguments:
liquiditytype - liquidity in positionsptype - sqrt of current pricesatype - sqrt of lower tick pricesbtype - sqrt of upper tick price
Returns:
number of tokens0 in tick bin
calculate_token1_amount
def calculate_token1_amount(liquidity, sp, sa, sb)
Calculate the number of tokens1 inside an individual tick bin
Arguments:
liquiditytype - liquidity in positionsptype - sqrt of current pricesatype - sqrt of lower tick pricesbtype - sqrt of upper tick price
Returns:
number of tokens1 in tick bin
calculate_liquidity0
def calculate_liquidity0(amount0, sa, sb)
calculate_liquidity1
def calculate_liquidity1(amount1, sa, sb)
calculate_liquidity
def calculate_liquidity(amount0, amount1, sp, sa, sb)
get_position_info
def get_position_info(position_id: int,
block_identifier: Optional[int] = None) -> Tuple
get_positions
def get_positions(agent_address: str,
block_identifier: Optional[int] = None) -> List[int]
get indices of agent's LP positions (i.e. position IDs)
get_pool_spot_rate
def get_pool_spot_rate(pool_address: str,
inverted: bool = False,
block_identifier=None) -> float
get_pool_current_tick
def get_pool_current_tick(pool_address: str) -> int
get_pool
def get_pool(token0_address: str, token1_address: str, fee: int)
is_checksum_address
def is_checksum_address(address: str) -> bool
to_checksum_address
def to_checksum_address(address: str) -> str
get_pool_liquidity
def get_pool_liquidity(pool_address: str)
get_transaction_fees
def get_transaction_fees(block_identifier=None) -> Dict[str, int]
get_token_contract
def get_token_contract(token_address: str) -> Contract
get_pool_contract
def get_pool_contract(pool_address: str) -> Contract
pool_price_from_current_tick
def pool_price_from_current_tick(token0_address: str,
token1_address: str,
pool_fee: int,
block_identifier=None) -> Tuple[float, int]
Return the price of the current tick, in the order of the token input addresses.
Arguments:
token0_addressstr - descriptiontoken1_addressstr - descriptionpool_feeint - description
Returns:
float- the price of the pool in the order of the input tokens.int- the current tick of the pool.
unsigned_modulo
def unsigned_modulo(python_int: int, num_bits: int) -> int
Fixed point arithmetic on blockchain is not implemented in python. We need to modulo a python integer into an unsigned int of num_bits.
NOTE: implemented via bitwise.
Arguments:
python_intint - any python int.num_bitsint - The number of bits that the unsigned int needs to be.
Returns:
int- An integer in the range [0,..., 2^num_bits -1] e.g,. an unsigned int.
get_min_tick
def get_min_tick(fee: int) -> int
get_max_tick
def get_max_tick(fee: int) -> int
default_tick_range
def default_tick_range(fee: int) -> Tuple[int, int]
nearest_tick
def nearest_tick(tick: int, fee: int) -> int
Return the nearest tick on the uniswap v3 pool.
price_to_tick
def price_to_tick(price: float, token0_decimals: int,
token1_decimals: int) -> int
Converts a price in nominal units to a tick value on uniswap v3
Arguments:
price (float): The price of token0 to token1 in nominal units. E.g., 1eth = 1500.01 usdt
token0_decimalsint - decimals of token0token1_decimalsint - decimals of token1
Returns:
int: the tick value for the inputted price
price_to_sqrtp
def price_to_sqrtp(price)
sqrtp_to_price
def sqrtp_to_price(sqrtp)
tick_to_unadjusted_sqrtp
def tick_to_unadjusted_sqrtp(tick: int) -> float
Convert a tick to an unadjusted sqrt price. Unadjusted means no token decimals multiplication has been performed.
Arguments:
tickint - A tick bin.
Returns:
unadjusted_price: The price that has not been adjusted yet. See 3.3.2 of LIQUIDITY MATH IN UNISWAP V3 by Atis Elsts
NOTE- To convert to an actual price, need to multiply by token0.decimals - token1.decimals as in tick_to_price() function
tick_to_price
def tick_to_price(tick: int, token0_decimals: int,
token1_decimals: int) -> float
Determine the price given a tick value.
Arguments:
tickint - the tick of the pool from which to calculate price token0_decimals (int): token1_decimals (int):
tick_to_sqrt_price
def tick_to_sqrt_price(tick: int) -> float
tick_to_sqrt_price_x_96
def tick_to_sqrt_price_x_96(tick)
sqrt_price_x_96_to_tick
def sqrt_price_x_96_to_tick(sqrt_price_x_96)
price_x96_to_price
def price_x96_to_price(sqrt_price_x_96, decimals_diff=12)
trunc_int
def trunc_int(num: int) -> int
get_next_ticks
def get_next_ticks(pool_address: str,
fee: int,
buffer: int,
side_lower: bool,
tick: int = None)
This function calculates from either the current tick or a given tick, the next valid ticks based on the buffer.
Arguments:
pool_address(str): The address of the pool.fee(int): The fee of the pool.buffer(int): The number of bins or tick spacing to "skip".side_lower(bool): The side of the buffer relative to the tick.tick(int): None will default to the current tick from on-chain fetching.
lp_fees_percent_of_current_tokens
def lp_fees_percent_of_current_tokens(
pool_address: str,
position_id: int,
block_identifier: Optional[int] = None) -> Tuple[float, float]
calculate_impermanent_loss
def calculate_impermanent_loss(price: float,
strike_price: float,
lower_limit: float,
upper_limit: float,
dex: str = "uniswap_v3") -> float
Calculate the impermanent loss for a single Uniswap V3 position.
NOTE: Impermanent loss is defined to be a negative number here.
Arguments:
pricefloat - price (current)strike_pricefloat - "strike price" (i.e. price the position was opened at)lower_limitfloat - lower bound of Uniswap V3 position rangeupper_limitfloat - upper bound of Uniswap V3 position rangedexstr - DEX in which to calculate IL (e.g. 'uniswap_v2', default == 'uniswap_v3')
Returns:
float- impermanent loss as a fraction. To get a percentage, multiply by 100
calculate_open_position_amount
def calculate_open_position_amount(
token0_amount: int,
token1_amount: int,
lower_price: float,
upper_price: float,
pool_address: str,
slippage_spot_rate: float,
adjust_amounts: bool = True,
adjust_threshold: float = 0.001,
block_identifier: Optional[int] = None
) -> Tuple[Tuple[int, int], Tuple[float, float], float]
Calculate the amount of tokens to be deposited into a Uniswap V3 position. Input a range of spot price that could be realized in the future, and output a range of liquidity amounts that could be deposited into the position.
calculate_open_position_amount_with_spot_rate
def calculate_open_position_amount_with_spot_rate(
token0_amount: int,
token1_amount: int,
lower_price: float,
upper_price: float,
pool_address: str,
spot_rate: float,
adjust_amounts: bool = True,
adjust_threshold: float = 0.001
) -> Tuple[Tuple[int, int], Tuple[float, float]]