[go: up one dir, main page]

0% found this document useful (0 votes)
233 views20 pages

Shadow Exchange Bot - Data Extraction

The document provides a comprehensive analysis of the NFT Position Manager contract on Shadow Exchange, detailing its purpose in managing non-fungible tokens representing liquidity positions in Ramses V3 pools. It outlines key interfaces, functions for creating, managing, and closing positions, as well as mechanisms for fee collection and price retrieval. The document emphasizes the importance of understanding these elements for the development of an automated position manager bot.

Uploaded by

Robin Patterson
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
233 views20 pages

Shadow Exchange Bot - Data Extraction

The document provides a comprehensive analysis of the NFT Position Manager contract on Shadow Exchange, detailing its purpose in managing non-fungible tokens representing liquidity positions in Ramses V3 pools. It outlines key interfaces, functions for creating, managing, and closing positions, as well as mechanisms for fee collection and price retrieval. The document emphasizes the importance of understanding these elements for the development of an automated position manager bot.

Uploaded by

Robin Patterson
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

Technical Specifications for Automated Position Manager Bot

on Shadow Exchange
2. Core Contracts Analysis

To facilitate the development of an automated position manager bot for Shadow


Exchange, a thorough understanding of the core smart contracts is essential. This
section outlines the key contracts involved, their purposes, and their respective
interfaces.

Contract Name Address Purpose Key Interfaces

NFT Position 0x12e66c8f215ddd5d Manages INonfungiblePosition


Manager 48d150c8f46ad0c6fb non-fungible tokens Manager
0f4406 representing liquidity
positions in Ramses
V3 pools, handling
creation,
modification, and fee
collection.

The central contract for managing liquidity positions is the NFT Position Manager at
address 0x12e66c8f215ddd5d48d150c8f46ad0c6fb0f4406 [user_query]. Analysis of
the contract code 1 indicates that it is the primary implementation for the
non-fungible token that wraps and manages positions within Ramses V3 pools, which
are likely compatible with the Uniswap V3 protocol. This contract facilitates the
creation of new liquidity positions, the adjustment of existing positions (increasing or
decreasing liquidity), and the collection of fees earned by these positions. Given the
NFT nature of these positions, each instance of liquidity provision is a unique and
trackable asset, adhering to the ERC-721 standard.1 This allows for individual
management and potentially transfer of these positions.

The key interface for interacting with this contract is likely


INonfungiblePositionManager. Research suggests a strong connection to Uniswap
V3's periphery contracts 2, and the contract code analysis further supports this
association.1 The INonfungiblePositionManager interface, as found in Uniswap V3's
periphery repository 5, outlines the functions necessary for managing NFT positions
and interacting with the underlying liquidity pools. The complete Application Binary
Interface (ABI) for the NFT Position Manager contract on Sonic can be found in 1,
providing the essential blueprint for programmatic interaction. Unfortunately, direct
examination of the "Read Contract" and "Write Contract" tabs on the Sonic
blockchain explorer was not possible due to website inaccessibility.12 This necessitates
a deeper reliance on the contract code and its ABI for understanding the available
functionalities.

The contract code itself is extensive, comprising 56 Solidity files.1 The core logic for
position management resides in NonfungiblePositionManager.sol. Other important
components include implementations of the ERC-721 standard, libraries for handling
fixed-point arithmetic (FixedPoint128, FixedPoint96), performing full math operations
(FullMath), computing pool addresses (PoolAddress), and managing liquidity
(LiquidityManagement). Additionally, the presence of interfaces such as
IRamsesV3Pool, IGaugeV3, IVoter, and IAccessHub indicates a complex ecosystem
where the position manager interacts with various other contracts for functionalities
like accessing pool state, handling incentives, managing governance, and controlling
access.1 The standard ERC-721 functions within the ABI 1, such as ownerOf,
transferFrom, safeTransferFrom, approve, and setApprovalForAll, confirm the
standard ownership model for the position NFTs.

The NFT Position Manager contract interacts with pool contracts, likely conforming to
the IRamsesV3Pool interface, to manage liquidity for specific token pairs and fee tiers.
The mint function, for instance, requires parameters like token0, token1, and fee,
which are standard identifiers for pools in Uniswap V3-like systems.5 The presence of
the createAndInitializePoolIfNecessary function 1 further highlights the contract's
capability to interact with and potentially bootstrap new pools. While a dedicated
router contract address is not explicitly provided, the functionalities for pool
interaction and the potential need for swaps within a position management strategy
imply the existence of such a component, possibly adhering to the ISwapRouter
interface as seen in Uniswap V3.7

Furthermore, the NFT Position Manager relies on several periphery contracts for
various supporting functionalities. These include managing liquidity safely
(LiquidityManagement.sol), accessing immutable state variables like the factory
address and WETH9 address (PeripheryImmutableState.sol,
IPeripheryImmutableState.sol 15), validating transaction parameters
(PeripheryValidation.sol), creating and initializing pools (PoolInitializer.sol,
IPoolInitializer.sol 16), and handling payments involving ETH and other tokens
(IPeripheryPayments.sol 23). The INonfungiblePositionManager interface extends
IPeripheryPayments and IPeripheryImmutableState 5, underscoring the importance of
these periphery functionalities for position management. Functions like refundETH,
sweepToken, and unwrapWETH9 in the ABI 1 are indicative of the payment handling
capabilities provided by these periphery contracts.

3. Detailed Function Reference

To effectively build an automated position manager, a detailed understanding of the


core functions within the NFT Position Manager contract is crucial. This section
outlines the key functions for creating, closing, checking, and managing liquidity
positions.
●​ Position Creation Functions:
○​ mint(INonfungiblePositionManager.MintParams params): This function, as
described in 2, is responsible for creating a new concentrated liquidity
position. This position is then wrapped in a non-fungible token (NFT). The
function is intended to be called when the underlying pool for the specified
tokens and fee already exists and has been initialized. It's important to note
that if a pool is created but not yet initialized, a separate initialization method
would need to be invoked first. The full signature of this function, as per the
contract's ABI 1, is mint(struct INonfungiblePositionManager.MintParams
params). The parameters required for this function, detailed in 5, are
encapsulated within the MintParams struct and include: token0 (address of
the first token), token1 (address of the second token), fee (the fee tier of the
pool), tickLower (the lower bound of the price range in ticks), tickUpper (the
upper bound of the price range in ticks), amount0Desired (the desired
amount of token0 to provide), amount1Desired (the desired amount of token1
to provide), amount0Min (the minimum amount of token0 acceptable to
provide, serving as slippage control), amount1Min (the minimum amount of
token1 acceptable to provide, also for slippage control), recipient (the address
that will receive the newly minted NFT), and deadline (a timestamp after which
the transaction will no longer be valid). The function returns a tuple containing
tokenId (the unique identifier of the newly minted NFT representing the
position), liquidity (the amount of liquidity added to the pool within the
specified range), amount0 (the actual amount of token0 provided), and
amount1 (the actual amount of token1 provided). The gas requirements for
this function will vary depending on factors such as whether the pool needs to
be initialized and the complexity of storage operations. Error handling
typically involves reverting the transaction if conditions like insufficient funds
or deadline expiry are met, or if custom errors defined within the contract are
triggered. This function is central to the bot's ability to establish new liquidity
positions based on defined strategies and market conditions.
○​ createAndInitializePoolIfNecessary(address token0, address token1,
int24 tickSpacing, uint160 sqrtPriceX96): This function, found in the
contract's ABI 1 and mentioned in 17, serves the purpose of creating a new
liquidity pool if one does not already exist for the given token pair and fee,
and then initializing it if it hasn't been initialized previously. The full signature
is createAndInitializePoolIfNecessary(address token0, address token1, int24
tickSpacing, uint160 sqrtPriceX96). The parameters required are token0
(address of the first token), token1 (address of the second token), tickSpacing
(the tick spacing for the pool, which determines the minimum increment
between valid ticks 25), and sqrtPriceX96 (the initial square root price of the
pool encoded as a Q64.96 value). The function returns the address of the pool
(pool). The gas cost for this function is expected to be higher, especially if a
new pool needs to be deployed and initialized, as this involves more complex
on-chain operations. Error handling will likely include reverting if a pool with
the same parameters already exists or if the provided initial price is invalid.
This function is essential for scenarios where the bot needs to provide
liquidity in a pool that has not yet been created. The tickSpacing parameter is
particularly important in the context of concentrated liquidity, as it defines the
granularity of price levels at which liquidity can be added.25
●​ Position Closing/Collecting Functions:
○​ decreaseLiquidity(INonfungiblePositionManager.DecreaseLiquidityPara
ms params): Described in 2, this function allows for the reduction of liquidity
within an existing position. The full signature from the ABI 1 is
decreaseLiquidity(struct
INonfungiblePositionManager.DecreaseLiquidityParams params). The
parameters, as outlined in 5, are within the DecreaseLiquidityParams struct
and include: tokenId (the identifier of the NFT representing the position),
liquidity (the amount of liquidity to remove), amount0Min (the minimum
amount of token0 expected as a result of the liquidity decrease, for slippage
control), amount1Min (the minimum amount of token1 expected, also for
slippage control), and deadline (a timestamp after which the transaction will
no longer be valid). The function returns the amounts of tokens accounted for
the decrease in liquidity: amount0 (uint256) and amount1 (uint256). The gas
requirements will depend on the amount of liquidity being removed and the
extent of state updates required. Analysis of the position closing transaction
[user_query] should provide more specific gas usage details. Error handling
may involve reverting if the tokenId is invalid, if the requested liquidity
decrease exceeds the position's current liquidity, or if the deadline has
passed. This function is crucial for the bot to manage its active positions by
reducing exposure or adjusting the amount of liquidity provided.
○​ collect(INonfungiblePositionManager.CollectParams params): This
function, detailed in 2, enables the collection of any accrued fees for a specific
liquidity position NFT. The full signature from the ABI 1 is collect(struct
INonfungiblePositionManager.CollectParams params). The parameters, within
the CollectParams struct 5, are: tokenId (the identifier of the NFT), recipient
(the address that will receive the collected fees), amount0Max (the maximum
amount of token0 to collect), and amount1Max (the maximum amount of
token1 to collect). The function returns the actual amounts of fees collected:
amount0 (uint256) and amount1 (uint256). The gas cost for this operation is
generally lower compared to functions that modify liquidity. Error handling will
likely include reverting if the provided tokenId is invalid. This function is
essential for the bot to realize the earnings from its liquidity positions by
claiming the accumulated fees. The amount0Max and amount1Max
parameters allow for control over the amount of each token collected in a
single transaction.
○​ burn(uint256 tokenId): As described in 2, this function allows for the
complete closure of a liquidity position by burning the corresponding NFT.
The full signature from the ABI 1 is burn(uint256 tokenId). The only parameter
required is tokenId (the identifier of the NFT to burn). This function does not
return any values. The gas requirements for burning an NFT might be relatively
high as it involves storage deletion on the blockchain. Error handling will likely
include checks to ensure the tokenId is valid and that the position associated
with it has zero remaining liquidity and no outstanding fees to be collected.
This function represents the final step in closing a position managed by the
bot.
●​ Position Status Checking Functions:
○​ positions(uint256 tokenId): This function, described in 1, allows for the
retrieval of detailed information about a specific liquidity position given its
token ID. The full signature from the ABI 1 is positions(uint256 tokenId). The
single parameter is tokenId (the identifier of the NFT). The function returns a
comprehensive struct containing the following information: token0 (address
of the first token in the pool), token1 (address of the second token),
tickSpacing (the tick spacing of the pool), tickLower (the lower tick of the
position's range), tickUpper (the upper tick of the position's range), liquidity
(the amount of liquidity provided), feeGrowthInside0LastX128 (the
accumulated fee growth for token0 inside the position's range at the time of
the last action), feeGrowthInside1LastX128 (the accumulated fee growth for
token1 inside the range at the last action), tokensOwed0 (the amount of
token0 fees yet to be collected), and tokensOwed1 (the amount of token1 fees
yet to be collected). As a view function, the gas cost for calling this function is
minimal. Error handling will likely involve reverting if the provided tokenId does
not correspond to an existing position. This function is crucial for the bot to
monitor the state of its managed positions, including their price ranges,
liquidity amounts, and accrued fees, enabling informed decision-making for
adjustments or closures.
●​ Fee Collection Mechanisms:
○​ The primary mechanism for collecting fees is through the collect function, as
detailed previously. The fees earned by a liquidity position are tracked within
the positions struct, specifically in the tokensOwed0 and tokensOwed1 fields.1
These values represent the uncollected amounts of each token that the
position has earned. The calculation of these earned fees relies on the
feeGrowthInside0LastX128 and feeGrowthInside1LastX128 variables, which
store the cumulative fee growth within the position's tick range at the time of
the last interaction with the position (such as minting, increasing liquidity, or
decreasing liquidity).2 When a swap occurs in the underlying pool within the
active price range of a position, fees are generated and proportionally
attributed to the liquidity within that range. The feeGrowthInside variables are
updated to reflect this accumulation. By comparing the current
feeGrowthInside values of the pool with the feeGrowthInsideLastX128 values
stored for the position, the contract can determine the amount of fees that
have accrued since the last action on the position. The collect function then
facilitates the transfer of these owed tokens to the specified recipient, up to
the maximum amounts specified in the parameters. This integrated fee
collection mechanism ensures that liquidity providers are incentivized for their
participation in the market.
●​ Price Retrieval Functions:
○​ The NonfungiblePositionManager contract itself does not directly provide
functions for retrieving the current price of tokens in a pool. Instead, price
information is derived from the state of the underlying pool contract, which
likely conforms to the IRamsesV3Pool interface. The price within a Uniswap
V3-like pool is represented by the current tick.25 To obtain the current tick, the
bot would need to interact with the relevant pool contract. Snippet 24
demonstrates interaction with an IRamsesV3Pool contract to retrieve the
square root price (sqrtPriceX96). Once the current tick is obtained from the
pool, it needs to be converted into a human-readable price. This conversion is
facilitated by the TickMath library, which is identified as file 41 in the contract
code analysis 1 and is a standard component of Uniswap V3 core.6 The
TickMath library contains functions to convert between tick values and the
square root of the price (sqrtPriceX96), which is a fixed-point Q64.96 number.
To get the actual price ratio (token1 per token0), this sqrtPriceX96 value
needs to be divided by 2<sup>96</sup> and then squared. Snippet 28 provides
some context on the mathematical basis of the TickMath library. Therefore,
the bot's price retrieval mechanism would involve first identifying the correct
pool contract for the desired token pair, calling a function on that pool to get
the current tick or square root price, and then utilizing the TickMath library to
perform the necessary conversions to obtain a usable price value. This
process is essential for the bot to determine if a position is currently within its
specified price range and to inform decisions about when to adjust or close
positions based on market movements.

4. On-Chain Data Structures

Understanding how data is structured on-chain is crucial for the bot to interpret and
interact with the smart contracts effectively. This section details the key data
structures used in the NFT Position Manager contract.
●​ Position Representation: The core information about a liquidity position is
stored in a struct named Position within the NonfungiblePositionManager
contract.1 This struct can be accessed for a given tokenId using the
positions(uint256 tokenId) function.1 The Position struct comprises the following
fields:
○​ token0 (address): The contract address of the first token in the liquidity pool.
○​ token1 (address): The contract address of the second token in the liquidity
pool.
○​ tickSpacing (int24): The tick spacing of the pool in which the position resides.
This value determines the minimum difference between valid tick values.25
○​ tickLower (int24): The lower boundary of the price range for this liquidity
position, represented as a tick index.
○​ tickUpper (int24): The upper boundary of the price range for this liquidity
position, also represented as a tick index.
○​ liquidity (uint128): The amount of liquidity provided within the specified price
range. This is a core metric representing the position's contribution to the
pool.
○​ feeGrowthInside0LastX128 (uint256): The cumulative fee growth per unit of
liquidity for token0 that has occurred within the position's tick range up to the
last time the position was modified or interacted with. This value is used to
calculate the fees earned by the position.
○​ feeGrowthInside1LastX128 (uint256): Similar to the above, but for token1.
○​ tokensOwed0 (uint128): The amount of token0 fees that have accrued to this
position and are yet to be collected.
○​ tokensOwed1 (uint128): The amount of token1 fees yet to be collected. These
fields collectively provide a comprehensive snapshot of a user's liquidity
position, including the tokens involved, the price range where liquidity is
provided, the amount of liquidity, and the accrued but uncollected fees.3 This
structure is fundamental for the bot to understand and manage individual
liquidity positions.
●​ Tick Spacing and Range Calculation: The tickSpacing is a parameter
associated with the pool itself and dictates the valid increments between ticks
where liquidity can be placed.25 It is stored within the Position struct, allowing the
bot to know the granularity of the price range for a given position. The price
range of a position is explicitly defined by the tickLower and tickUpper values,
which are integer indices representing discrete price levels. To determine these
tick boundaries for a desired price range, the bot will need to utilize the TickMath
library.1 This library provides functions to convert between token prices and their
corresponding tick values. The process typically involves converting the desired
lower and upper price bounds into their respective tick values, taking into account
the tickSpacing of the pool to ensure that the chosen ticks are valid for liquidity
provision. Accurate calculation of these tick boundaries is essential for the bot to
create and manage positions within the intended price ranges, thereby
maximizing capital efficiency in the concentrated liquidity model.
●​ Price Representation and Conversion: Within the smart contracts, prices are
primarily represented as the square root of the price (of token1 relative to token0)
encoded as a fixed-point number with 96 fractional bits (Q64.96 format), denoted
as sqrtPriceX96.24 This format is optimized for on-chain calculations, particularly
within the core swapping logic of the protocol. The TickMath library 1 is crucial for
converting between this sqrtPriceX96 representation and the more
human-readable tick values. The library's getSqrtRatioAtTick function takes a tick
index (int24) as input and returns the corresponding sqrtPriceX96 value.
Conversely, the getTickAtSqrtRatio function takes a sqrtPriceX96 value and
returns the corresponding tick. For the bot to work with prices in a more intuitive
format (e.g., decimal values), it will need to perform further conversion. This
typically involves taking the sqrtPriceX96 value, dividing it by 2<sup>96</sup> to
get the actual square root of the price, and then squaring the result to obtain the
price ratio of token1 per unit of token0. Understanding this price representation
and the conversion process using the TickMath library is fundamental for the bot
to interpret market prices and set appropriate price ranges for liquidity positions.
●​ Special Data Types: The smart contracts within the Shadow Exchange
ecosystem, particularly the NFT Position Manager and related libraries, make
extensive use of specific data types optimized for efficiency and precision on the
Ethereum Virtual Machine (EVM). These include:
○​ uint128: Used for representing amounts of liquidity. This data type allows for
large enough values while being more gas-efficient than uint256 for certain
operations.
○​ int24: Used to represent tick values. The limited range of this signed integer
type is sufficient for the price ranges supported by the protocol and helps in
saving gas.
○​ uint160: Employed for the sqrtPriceX96 value. This size is chosen to provide
the necessary precision for price calculations in the Q64.96 format.
○​ uint256: The standard unsigned integer type used for larger token amounts
and other general-purpose integer storage. Furthermore, the protocol heavily
relies on fixed-point arithmetic. The Q64.96 format for sqrtPriceX96 indicates
64 bits for the integer part and 96 bits for the fractional part, providing high
precision for price representation. Similarly, the TickMath library utilizes
Q128.128 format internally for intermediate calculations.1 The bot's
implementation will need to correctly handle these specific data types and
understand the principles of fixed-point arithmetic to ensure accurate
interactions with the smart contracts and reliable position management.

5. Position Management Logic

The core of the automated position manager bot lies in its ability to implement the
logic for creating, monitoring, and adjusting liquidity positions. This section details the
key mechanisms involved in position management.
●​ In-Range Check Mechanism: A fundamental aspect of managing concentrated
liquidity positions is determining whether the current market price falls within the
price range defined by a position's tickLower and tickUpper values. Fees are only
earned when swaps occur within this range.25 To implement this check, the bot
needs to perform the following steps: First, it must retrieve the current tick index
for the relevant pool. This can be done by querying the pool contract (likely
implementing IRamsesV3Pool) for its current state, which includes the current
tick. Second, the bot needs to retrieve the tickLower and tickUpper values for the
specific position it is managing. These values are obtained from the Position
struct by calling the positions(uint256 tokenId) function on the NFT Position
Manager contract.1 Finally, the bot compares the current tick with the tickLower
and tickUpper values. If the current tick is greater than or equal to tickLower and
less than or equal to tickUpper, the position is considered to be in-range and is
actively earning fees. This check is crucial for the bot to assess the performance
of its positions and to decide whether adjustments to the price range are
necessary based on market movements.
●​ Position Boundary Calculation: The price range of a concentrated liquidity
position is defined by its lower and upper tick boundaries (tickLower and
tickUpper). The bot needs a mechanism to calculate these tick values based on a
desired price range. This process involves converting the desired lower and upper
price limits into their corresponding tick indices. The TickMath library 1 provides
the necessary functions for this conversion. The bot would typically start with a
desired price range, perhaps based on a trading strategy or analysis of market
volatility. It would then use the getTickAtSqrtRatio function from the TickMath
library to convert the lower price bound into a tickLower value and the upper
price bound into a tickUpper value. It is also important to consider the tickSpacing
of the pool.25 The calculated tick values should ideally be multiples of the pool's
tickSpacing to ensure they are valid for liquidity provision. The bot's logic would
need to handle this constraint, possibly by rounding the calculated tick values to
the nearest multiple of the tickSpacing. Accurate calculation of these tick
boundaries is essential for the bot to create positions that align with its intended
strategy, whether it's providing liquidity around the current market price or in a
wider range to capture more potential fee-generating swaps.
●​ Liquidity Calculation and Distribution: When creating a new liquidity position
or adjusting an existing one, the bot needs to determine the amount of each
token (token0 and token1) required to achieve a desired amount of liquidity within
a specific price range. This calculation involves considering the tick boundaries
(tickLower and tickUpper) and the desired liquidity amount. The exact formulas
for this calculation are likely implemented within the LiquidityManagement.sol
library 1 or within the core Ramses V3 pool contract logic. Snippet 8 and 44 might
offer some high-level context, but the precise mathematical details would need to
be derived from the contract code. Generally, for a given price range, the amount
of liquidity that can be provided is determined by the reserves of the two tokens
available and the constant product formula adapted for concentrated liquidity.
When the bot specifies a desired liquidity amount and a price range (in ticks), the
underlying smart contracts calculate the corresponding amounts of token0 and
token1 that the user (or the bot) needs to provide. Conversely, when liquidity is
added, it is distributed proportionally across the price range defined by the ticks.
The bot's implementation will need to correctly invoke the mint or
increaseLiquidity functions on the NFT Position Manager contract with the
calculated token amounts to achieve the desired liquidity within the specified tick
range.
●​ Tick to Price Conversion: While the smart contracts primarily use ticks and
sqrtPriceX96 for internal calculations, for the bot to interpret these values in
terms of actual token prices and to define price ranges based on market analysis,
it needs to be able to convert between ticks and human-readable prices. The
TickMath library 1 provides the getSqrtRatioAtTick function, which takes a tick
index (int24) as input and returns the square root of the price (sqrtPriceX96). To
get the actual price ratio (e.g., the price of token1 in terms of token0), the bot
would need to take this sqrtPriceX96 value, divide it by 2<sup>96</sup>, and then
square the result. For example, if the bot retrieves the tickLower and tickUpper
values for a position, it can use getSqrtRatioAtTick to get the corresponding
square root prices. These square root prices can then be converted into decimal
price values, allowing the bot to understand the actual price range within which
the liquidity is being provided. This conversion is essential for the bot to make
strategic decisions about setting and adjusting position boundaries based on
market prices and trading strategies.

6. Subgraph and API Information

To enhance the functionality and efficiency of the automated position manager bot,
leveraging off-chain data sources such as subgraphs and APIs is highly beneficial.
This section outlines the available resources for Shadow Exchange on the Sonic
blockchain.
●​ Shadow Exchange Subgraph Details: Several resources indicate the presence
of subgraphs for Shadow Exchange, which operate under the name
ShadowSwap. A GitHub repository titled Shadow-Subgraphs 34 hosts these
subgraphs, providing GraphQL endpoints to query events and entities within the
Core Chain and ShadowSwap ecosystem. The repository lists multiple subgraphs,
including Exchange, which tracks data on price, volume, and liquidity; Pairs, which
tracks information on ShadowSwap pairs and tokens; and others like Blocks,
Shadow Puppet, SmartChef, Timelock, and ShadowChef (v2). This suggests that a
wealth of indexed on-chain data is available, which the bot can utilize to monitor
pool statistics, historical prices, and the overall activity on Shadow Exchange.
Another GitHub repository related to Shadow Exchange 35 mentions a subgraph
scraper, indicating community efforts to collect and analyze data. Furthermore,
Dune Analytics 36, a popular platform for blockchain data analysis, also supports
Shadow Exchange, likely providing dashboards and the ability to create custom
queries. The existence of these subgraphs and data platforms means the bot can
efficiently access historical and real-time data without needing to directly query
blockchain nodes, which can be resource-intensive and slower. The GraphQL
endpoints for the subgraphs hosted in the Shadow-Subgraphs repository 34 would
be the primary interface for the bot to query this indexed data.
●​ Real-Time Price Data APIs: For the bot to make informed decisions about
position management, having access to real-time price data for the tokens traded
on Shadow Exchange is crucial. Several cryptocurrency data aggregators provide
APIs that can be used for this purpose. CoinGecko 37 is identified as a potential
source for Shadow Exchange price data. Other platforms like Rootdata 40, CoinEx
41
, and DefiLlama 42 also provide price and market information for various
cryptocurrencies, including those traded on decentralized exchanges. These APIs
typically offer endpoints to retrieve the current price of a token, often paired with
other relevant data such as trading volume, market capitalization, and price
history. The bot can integrate with one or more of these APIs to get real-time
price feeds for the specific token pairs it is managing liquidity for. The specific API
endpoints, query formats, and response structures will vary depending on the
chosen provider, and the bot's implementation will need to be tailored to these
specifications.
●​ Other Indexers or Data Services: Beyond dedicated subgraphs and price APIs,
other data services might offer valuable resources for the bot. As mentioned
earlier, Dune Analytics 36 provides a powerful platform for querying and visualizing
blockchain data, including data from Shadow Exchange. This could be useful for
the bot to access aggregated statistics, identify trends, or even use as a source
for historical price data if needed. Additionally, Alchemy Subgraphs 43 are
reported to be available on the Sonic blockchain. Alchemy is a blockchain
development platform that offers enhanced indexing capabilities with faster
speeds and lower latency compared to some other subgraph providers. If Shadow
Exchange data is indexed using Alchemy Subgraphs, this could be another
valuable resource for the bot to consider for accessing reliable and up-to-date
on-chain information. Exploring the documentation and available APIs of these
platforms could reveal additional data points or functionalities that the bot could
leverage to improve its position management strategies.

7. Transaction Analysis

Analyzing specific transactions on the Sonic blockchain can provide valuable insights
into how the NFT Position Manager contract is used in practice. This section breaks
down the provided swap and position closing transactions.
●​ Swap Transaction Breakdown
(https://sonicscan.org/tx/0x1a86956eb0d90118114ff341d4ed8bf817d7f681a5fe143
5533d3841968f0719): This transaction needs to be examined directly on the
Sonicscan explorer to extract the function call details, input parameters, and gas
usage. Observing multiple swap transactions might reveal common patterns in
how these transactions are constructed on Shadow Exchange, such as the typical
function called on the router contract, the standard parameters used (e.g.,
slippage tolerances, recipient addresses), and the average gas costs associated
with swaps. This information can be useful for the bot if its strategy involves
performing swaps, for example, to rebalance liquidity positions or to manage the
tokens received from collected fees. Understanding the gas costs can also help in
optimizing the bot's transaction submissions.
●​ Position Closing Transaction Breakdown
(https://sonicscan.org/tx/0x5867518b99b2a68a94a46e5f147baa485a9512d0d74c5
56813d8af035da02267): Analyzing this transaction on the Sonicscan explorer will
provide direct information about how a liquidity position is closed on Shadow
Exchange. The key aspects to identify include:
○​ Exact Method Called: The specific function invoked on the NFT Position
Manager contract to initiate the closure. Based on the function reference, this
is likely to be either decreaseLiquidity or burn.
○​ Parameters Required: The exact parameters that were passed to the closing
function in this transaction. This will confirm the necessary inputs for the bot's
position closing logic.
○​ Associated Events Emitted: Any events emitted by the NFT Position Manager
contract (listed in 1) or other related contracts as a result of this transaction.
For a position closure, relevant events might include DecreaseLiquidity (if
liquidity is reduced), Collect (if fees are collected before or during closure),
Transfer (if the NFT ownership changes), or Burn (if the NFT is destroyed).
Examining the emitted events can provide confirmation of the actions taken
and the state changes that occurred.
○​ Checks or Validations Performed: By analyzing the transaction execution
details (if available on the explorer or by examining the contract code), it
might be possible to identify any specific checks or validations that the
contract performed before allowing the position to be closed. These could
include checks for sufficient remaining liquidity, ensuring all fees are
collected, or verifying the caller's permissions. Understanding these on-chain
checks is crucial for the bot to correctly execute position closures without
encountering errors.

8. Inter-Contract Dependencies and Interfaces

The NFT Position Manager contract relies on a network of other contracts and
libraries to perform its functions. Understanding these dependencies and the
interfaces they expose is crucial for the bot to interact correctly with the Shadow
Exchange ecosystem.

The NonfungiblePositionManager contract 1 imports and interacts with several other


contracts and libraries:
●​ IRamsesV3Pool (file 5): This interface 14 defines how the Position Manager
interacts with the underlying liquidity pools. It likely includes functions for getting
pool state (like current tick and liquidity), and potentially for performing actions
like collecting fees at the pool level.
●​ FixedPoint128 (file 6) and FixedPoint96 (file 51): These libraries provide
functionality for handling fixed-point numbers, which are essential for precise
calculations involving prices and liquidity.
●​ FullMath (file 7): This library contains functions for performing arithmetic
operations on large integers, handling potential overflows.
●​ INonfungiblePositionManager (file 85): This is the interface for the Position
Manager contract itself, defining the functions that can be called on it.
●​ INonfungibleTokenPositionDescriptor (file 99): This interface likely defines how
the metadata for the position NFTs is generated, possibly related to token URIs.
●​ PositionKey (file 10): This library provides a function to compute a unique key for
a position based on its properties, likely used for internal tracking.
●​ PoolAddress (file 11): This library offers functions for deterministically deriving
the address of a pool based on the involved tokens and fee.
●​ LiquidityManagement (file 12): This abstract contract likely contains internal
functions for safely managing liquidity within the pools, such as adding or
removing liquidity.
●​ PeripheryImmutableState (file 1315) and IPeripheryImmutableState (file 37):
These define and provide access to immutable state variables of the periphery
contracts, such as the address of the factory contract and the wrapped native
token (WETH9).
●​ PeripheryValidation (file 14): This abstract contract likely includes modifiers to
perform common validation checks, such as ensuring a transaction deadline has
not passed.
●​ PoolInitializer (file 1516) and IPoolInitializer (file 3517): These provide the
functionality to create and initialize new liquidity pools if they do not already exist.
●​ IGaugeV3 (file 1650): This interface defines how the Position Manager interacts
with Gauge V3 contracts, which are likely used for incentivizing liquidity provision
with reward tokens.
●​ IVoter (file 17): This interface outlines the functions for interacting with a Voter
contract, potentially involved in governance and directing reward emissions.
●​ IAccessHub (file 18): This interface defines the functions for an Access Hub
contract, likely used for managing access control and protocol parameters.
●​ Standard ERC-721 interfaces and implementations (files 2, 3, 19, 20, 21, 26,
47, 48): These provide the basic functionality for the non-fungible tokens
representing the liquidity positions.
●​ Utility libraries from OpenZeppelin (files 22, 23, 49, 50, 56): These include
libraries like Context (provides information about the transaction sender), Strings
(for string manipulation), Math and SignedMath (for mathematical operations),
and SafeCast (for safe type conversions).
●​ Uniswap V3 Core interfaces and libraries (files 5, 41, 42, 43): These include
IUniswapV3Pool (likely aliased as IRamsesV3Pool), TickMath 29 (for converting
between ticks and prices), CallbackValidation (for validating callbacks from pool
interactions), and LiquidityAmounts (for calculating liquidity amounts).
●​ Payment interfaces and libraries (files 36, 37, 53, 54): These include
IPeripheryPayments 23 (for handling ETH and token transfers), IWETH9 (interface
for the Wrapped Ether contract), and TransferHelper (for safely transferring
tokens).

Having the interfaces for these interacting contracts, such as


INonfungiblePositionManager 5, IRamsesV3Pool (whose structure can be inferred from
14
and the Uniswap V3 Core interface 18), IPeripheryPayments 23, IPoolInitializer 17, and
IGaugeV3 (whose functions can be inferred from the GaugeV3 implementation 50), is
essential for the bot to correctly encode function calls and decode responses when
interacting with these contracts on the Sonic blockchain. The interface for the
Uniswap V3 Factory (IUniswapV3Factory 53) might also be relevant for querying pool
information. Additionally, understanding the events emitted by the IUniswapV3Pool 46
can help the bot track pool activity.

10. Conclusion

The development of an automated position manager bot for Shadow Exchange on the
Sonic blockchain necessitates a deep understanding of the underlying smart
contracts, particularly the NFT Position Manager. This report has provided a
comprehensive overview of the core contracts, their functionalities, and the key
interfaces required for interaction. The analysis of the NFT Position Manager contract
(0x12e66c8f215ddd5d48d150c8f46ad0c6fb0f4406) reveals its role in managing
concentrated liquidity positions represented as NFTs, handling their creation,
modification, and fee collection.
The bot will need to interact with this contract using the INonfungiblePositionManager
interface, which shares significant similarities with Uniswap V3's counterpart. Key
functions such as mint, decreaseLiquidity, collect, and burn will be essential for
managing the lifecycle of liquidity positions. The positions function provides crucial
information about the state of each position. Understanding the fee collection
mechanisms and the process of price retrieval, which involves interacting with pool
contracts and utilizing the TickMath library, will be critical for the bot's operational
logic.

The on-chain data structures, including the Position struct, the representation of
prices as sqrtPriceX96, and the use of specific data types like uint128 and int24, need
to be correctly handled by the bot. The position management logic will involve
implementing mechanisms for checking if a position is in-range, calculating position
boundaries based on desired price levels and pool tick spacing, determining liquidity
amounts, and converting between tick values and prices.

Leveraging off-chain data sources such as the ShadowSwap subgraphs available on


GitHub and real-time price data APIs from platforms like CoinGecko and DefiLlama will
be crucial for the bot to monitor market conditions and make informed decisions.
Analyzing the provided swap and position closing transactions on the Sonic
blockchain explorer (once accessible) will offer practical insights into contract usage
and gas costs.

The NFT Position Manager's reliance on a multitude of other contracts and libraries,
including those for pool interaction, fixed-point arithmetic, pool initialization, and
periphery payments, highlights the complexity of the system. Having the correct
interfaces for these dependencies is essential for successful bot implementation.

Potential challenges in building this bot include the complexity of the concentrated
liquidity model, the need for precise calculations involving ticks and prices, and the
intricacies of interacting with multiple interconnected smart contracts. Further
investigation into the specifics of the Ramses V3 pool implementation and any
potential deviations from standard Uniswap V3 behavior will be necessary. Thorough
testing in a Sonic test environment is strongly recommended before deploying the bot
on the mainnet to ensure its reliability and effectiveness in managing liquidity
positions on Shadow Exchange.

Works cited

1.​ Shadow: Non fungible Position Manager NEW | Address ..., accessed April 9, 2025,
https://sonicscan.org/address/0x12e66c8f215ddd5d48d150c8f46ad0c6fb0f4406
#code
2.​ NonfungiblePositionManager - Uniswap Docs, accessed April 9, 2025,
https://docs.uniswap.org/contracts/v3/reference/periphery/NonfungiblePositionM
anager
3.​ v3-periphery/contracts/NonfungiblePositionManager.sol at main - GitHub,
accessed April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/NonfungiblePositio
nManager.sol
4.​ uniswapv3 package - github.com/maticnetwork/polygon-cli/bindings/uniswapv3 -
Go Packages, accessed April 9, 2025,
https://pkg.go.dev/github.com/maticnetwork/polygon-cli/bindings/uniswapv3
5.​ v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol ..., accessed
April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/INonfun
giblePositionManager.sol
6.​ The Full Contract - Uniswap Docs, accessed April 9, 2025,
https://docs.uniswap.org/contracts/v3/guides/providing-liquidity/the-full-contract
7.​ Set Up Your Contract - Uniswap Docs, accessed April 9, 2025,
https://docs.uniswap.org/contracts/v3/guides/providing-liquidity/setting-up
8.​ v3-periphery/contracts/libraries/PositionValue.sol at main - GitHub, accessed
April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/PositionVa
lue.sol
9.​ v3-periphery/contracts/interfaces/INonfungibleTokenPositionDescriptor.sol at
main - GitHub, accessed April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/INonfun
gibleTokenPositionDescriptor.sol
10.​v3-periphery/contracts/V3Migrator.sol at main - GitHub, accessed April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/V3Migrator.sol
11.​ v3-staker/contracts/interfaces/IUniswapV3Staker.sol at main - GitHub, accessed
April 9, 2025,
https://github.com/Uniswap/v3-staker/blob/main/contracts/interfaces/IUniswapV3
Staker.sol
12.​accessed January 1, 1970,
https://sonicscan.org/token/0x12e66c8f215ddd5d48d150c8f46ad0c6fb0f4406?a
=0x5F17e9D3B612262369CBf9F0fC141Eaa3D577e8e#readContract
13.​accessed January 1, 1970,
https://sonicscan.org/token/0x12e66c8f215ddd5d48d150c8f46ad0c6fb0f4406?a
=0x5F17e9D3B612262369CBf9F0fC141Eaa3D577e8e#writeContract
14.​v3-periphery/contracts/interfaces/ISwapRouter.sol at main - GitHub, accessed
April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/ISwapR
outer.sol
15.​v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol at main - GitHub,
accessed April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IPeriphe
ryImmutableState.sol/
16.​Smart Contracts Explorer - NonfungibleTokenPositionDescriptor - QuickNode,
accessed April 9, 2025,
https://www.quicknode.com/toolkit/smart-contracts/ethereum/mainnet/0x91ae84
2A5Ffd8d12023116943e72A606179294f3
17.​v3-periphery/contracts/interfaces/IPoolInitializer.sol at main - GitHub, accessed
April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IPoolIniti
alizer.sol
18.​v3-core/contracts/interfaces/IUniswapV3Pool.sol at main · Uniswap ..., accessed
April 9, 2025,
https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3P
ool.sol
19.​v3-periphery/contracts/base/PoolInitializer.sol at main - GitHub, accessed April 9,
2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/PoolInitializer.
sol
20.​v3-periphery/contracts/interfaces/IV3Migrator.sol at main - GitHub, accessed
April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IV3Migr
ator.sol
21.​Uniswap V3 Positions NFT-V1 (UNI-V3-POS) Token Tracker | OP Mainnet
Etherscan, accessed April 9, 2025,
https://optimistic.etherscan.io/token/0xc36442b4a4522e871399cd717abdd847ab
11fe88
22.​uniswap - Importing different solidity verion in your contract - Ethereum Stack
Exchange, accessed April 9, 2025,
https://ethereum.stackexchange.com/questions/131997/importing-different-solidi
ty-verion-in-your-contract
23.​v3-periphery/contracts/interfaces/IPeripheryPayments.sol at ... - GitHub,
accessed April 9, 2025,
https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/IPeriphe
ryPayments.sol
24.​NonfungibleTokenPositionDescri, accessed April 9, 2025,
https://vscode.blockscan.com/146/0x0d1eda94745bc38065cb803b340b8cc8f89
df33e
25.​Uniswap V3 ticks - dive into concentrated liquidity - MixBytes, accessed April 9,
2025, https://mixbytes.io/blog/uniswap-v3-ticks-dive-into-concentrated-liquidity
26.​Uniswap V3 on Cookbook, accessed April 9, 2025,
https://www.cookbook.dev/protocols/Uniswap-V3
27.​How does Uniswap v3-core derive the look up table used in TickMath?, accessed
April 9, 2025,
https://ethereum.stackexchange.com/questions/162021/how-does-uniswap-v3-c
ore-derive-the-look-up-table-used-in-tickmath
28.​How does Uniswap v3's logarithm library (TickMath.sol) work? - Ethereum Stack
Exchange, accessed April 9, 2025,
https://ethereum.stackexchange.com/questions/113844/how-does-uniswap-v3s-l
ogarithm-library-tickmath-sol-work
29.​v3-core/contracts/libraries/TickMath.sol at main · Uniswap/v3-core ..., accessed
April 9, 2025,
https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/TickMath.sol
30.​v3-core/contracts/libraries/Tick.sol at main - GitHub, accessed April 9, 2025,
https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/Tick.sol
31.​Error Codes - Uniswap Docs, accessed April 9, 2025,
https://docs.uniswap.org/contracts/v3/reference/error-codes
32.​When trying to implement a uniswapv3 example contract, I am not able to
compile it, accessed April 9, 2025,
https://stackoverflow.com/questions/78807334/when-trying-to-implement-a-uni
swapv3-example-contract-i-am-not-able-to-compile
33.​Ramses V3 - Consensys Diligence, accessed April 9, 2025,
https://diligence.consensys.io/audits/2024/08/ramses-v3/ramses-v3-audit-2024-
07.pdf
34.​SVerseLab/Shadow-Subgraphs - GitHub, accessed April 9, 2025,
https://github.com/SVerseLab/Shadow-Subgraphs
35.​harmony-one/shadow-scraper - GitHub, accessed April 9, 2025,
https://github.com/harmony-one/shadow-scraper
36.​Shadow Exchange - Dune, accessed April 9, 2025,
https://dune.com/shadow_exchange/main
37.​Shadow Exchange Statistics: Markets, Trading Volume & Trust Score | CoinGecko,
accessed April 9, 2025,
https://www.coingecko.com/en/exchanges/shadow-exchange
38.​SHADOW Price Today, Live Chart, USD converter, Market Capitalization |
CryptoRank.io, accessed April 9, 2025, https://cryptorank.io/price/shadow
39.​Shadow (SHADOW) Price Today - Charts, Prediction and Analysis - Token Metrics,
accessed April 9, 2025, https://app.tokenmetrics.com/en/shadow-2
40.​Shadow Exchange Project Introduction, Team, Financing and News_RootData,
accessed April 9, 2025,
https://www.rootdata.com/Projects/detail/Shadow%20Exchange?k=MTU3NjU%3D
41.​Shadow (SHADOW): Sonic's Liquidity Guide | CoinEx Academy, accessed April 9,
2025,
https://www.coinex.com/en/academy/detail/2189-shadow-shadow-the-ultimate-g
uide-to-sonics-concentrated-liquidity-exchange
42.​Shadow Exchange - DefiLlama, accessed April 9, 2025,
https://defillama.com/protocol/shadow-exchange
43.​Supercharge Your Development With Alchemy Subgraphs - Sonic Insights,
accessed April 9, 2025,
https://blog.soniclabs.com/supercharge-your-development-with-alchemy-subgr
aphs/
44.​2024-10-ramses-exchange/contracts/CL/core/RamsesV3Factory.sol at main ·
code-423n4 ... - GitHub, accessed April 9, 2025,
https://github.com/code-423n4/2024-10-ramses-exchange/blob/main/contracts/
CL/core/RamsesV3Factory.sol
45.​Ramses V3 - Consensys Diligence, accessed April 9, 2025,
https://diligence.consensys.io/audits/2024/08/ramses-v3/
46.​v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol at main - GitHub,
accessed April 9, 2025,
https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/pool/IUniswa
pV3PoolEvents.sol
47.​2024-10-ramses-exchange/contracts/CL/gauge/FeeCollector.sol at main -
GitHub, accessed April 9, 2025,
https://github.com/code-423n4/2024-10-ramses-exchange/blob/main/contracts/
CL/gauge/FeeCollector.sol
48.​aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol at master -
GitHub, accessed April 9, 2025,
https://github.com/aave/aave-v3-core/blob/master/contracts/interfaces/IPoolAddr
essesProvider.sol
49.​aave-v3-core/contracts/interfaces/IPool.sol at master - GitHub, accessed April 9,
2025,
https://github.com/aave/aave-v3-core/blob/master/contracts/interfaces/IPool.sol
50.​2024-10-ramses-exchange/contracts/CL/gauge/GaugeV3.sol at ..., accessed April
9, 2025,
https://github.com/code-423n4/2024-10-ramses-exchange/blob/main/contracts/
CL/gauge/GaugeV3.sol
51.​beta/interfaces/IUniswapV3Pool.sol at master - GitHub, accessed April 9, 2025,
https://github.com/beta-finance/beta/blob/master/interfaces/IUniswapV3Pool.sol
52.​chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol at
develop ... - GitHub, accessed April 9, 2025,
https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/sha
red/interfaces/AggregatorV3Interface.sol
53.​v3-core/contracts/interfaces/IUniswapV3Factory.sol at main - GitHub, accessed
April 9, 2025,
https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3F
actory.sol

You might also like