diff --git a/src/main/tjava/com/r35157/libs/raydium/Raydium.tjava b/src/main/tjava/com/r35157/libs/raydium/Raydium.tjava new file mode 100644 index 0000000..11213da --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/Raydium.tjava @@ -0,0 +1,220 @@ +package com.r35157.libs.raydium; + +import com.r35157.libs.valuetypes.basic.MoneyAmount; +import com.r35157.libs.valuetypes.basic.Range; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Set; + +public interface Raydium { + /** + * Fetches the current price for a Raydium liquidity pool. + * + *
The price is fetched from Raydium using the supplied pool id. The pool id may refer + * to any Raydium liquidity pool supported by the implementation.
+ * + * @param poolId the Raydium liquidity pool id + * @return the current pool price expressed as a Solana amount + * @throws IOException if the price could not be fetched or the response could not be parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the price + */ + ΩSolanaAmountΩ fetchPoolPrice(ΩRaydiumLiquidityPoolIdΩ poolId) throws IOException, InterruptedException; + + /** + * Fetches the Raydium liquidity pool ids where the supplied Solana owner address has positions. + * + *The returned set contains unique pool ids and does not guarantee iteration order.
+ * + *An implementation may discover pools through multiple Raydium position mechanisms, + * including standard liquidity pool token holdings and concentrated liquidity position NFTs. + * The returned ids are aggregated under the common {@code ΩRaydiumLiquidityPoolIdΩ} ValueTag.
+ * + * @param ownerAddress the Solana owner address to inspect + * @return the unique Raydium liquidity pool ids where the owner has a position + * @throws IOException if liquidity pool ids could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching liquidity pool ids + */ + Set<ΩRaydiumLiquidityPoolIdΩ> fetchLiquidityPoolIds(ΩSolanaAddressΩ ownerAddress) throws IOException, InterruptedException; + + /** + * Fetches the Raydium concentrated liquidity position NFT ids owned by a Solana address. + * + *The supplied owner address is inspected for token holdings that are candidates for + * Raydium concentrated liquidity position NFTs. The implementation verifies the candidates + * against Raydium's concentrated liquidity program before returning them.
+ * + *The returned set contains position NFT ids, not pool ids. Callers can use the returned + * NFT ids to fetch concentrated position state and then determine which pools the positions + * belong to.
+ * + * @param ownerAddress the Solana owner address to inspect + * @return the Raydium concentrated liquidity position NFT ids owned by the address + * @throws IOException if the position NFT ids could not be fetched or verified + * @throws InterruptedException if the calling thread is interrupted while fetching position NFT ids + */ + Set<ΩRaydiumLiquidityPoolPositionNftIdΩ> fetchConcentratedPositionNftIds( + ΩSolanaAddressΩ ownerAddress + ) throws IOException, InterruptedException; + + /** + * Fetches the total token amounts held by a standard Raydium liquidity pool. + * + *The returned amounts represent the pool-level token reserves for token A and + * token B. They do not describe a single liquidity provider's share of the pool.
+ * + * @param poolId the Raydium standard liquidity pool id + * @return the total token amounts held by the standard liquidity pool + * @throws IOException if the pool information could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the pool information + */ + RaydiumLiquidityPoolTokenAmounts fetchStandardLiquidityPoolTokenAmounts( + ΩRaydiumLiquidityPoolStandardIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Fetches the token amounts represented by an owner's position in a standard Raydium liquidity pool. + * + *The returned amounts describe the owner's calculated share of token A and token B in the + * standard liquidity pool. The calculation is based on the owner's liquidity pool token holdings, + * the LP mint supply and the pool-level token reserves returned by Raydium.
+ * + *Important: The returned amounts should be treated as an estimated pool-share + * view, not as an exact withdraw preview. Raydium's UI may show lower amounts when removing + * liquidity. For example, for pool {@code 8os8bnXoy5voKv3uBPPuVGyqWZGJaa2RRri5RbLUwPCY}, + * the pool-share calculation returned approximately {@code 2.1594651 EVE / 31.867153 USDT}, + * while Raydium's 100% withdraw preview showed approximately {@code 2.1062559 EVE / 31.55046 USDT}.
+ * + *// TODO To match Raydium's withdraw preview more precisely, this method should eventually use + * on-chain pool state and vault balances, excluding protocol, fund and creator fee buckets. + * Raydium's CPMM pool state contains fee fields and a {@code vault_amount_without_fee(...)} + * calculation that appears relevant for this. Until that is implemented, this method represents + * an estimate based on Raydium REST pool amounts and Solana LP supply.
+ * + *This method is specific to standard liquidity pools. Concentrated liquidity positions are + * represented differently and should use concentrated-position-specific methods.
+ * + * @param ownerAddress the Solana owner address whose standard pool position should be inspected + * @param poolId the Raydium standard liquidity pool id + * @return the estimated token amounts represented by the owner's standard pool position + * @throws IOException if the position information could not be fetched, calculated or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the position information + */ + RaydiumLiquidityPoolTokenAmounts fetchStandardLiquidityPoolPositionTokenAmounts( + ΩSolanaAddressΩ ownerAddress, + ΩRaydiumLiquidityPoolStandardIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Fetches the raw Raydium concentrated position state for a position NFT. + * + *The supplied NFT id identifies a concentrated liquidity position on Raydium. The returned + * state is a low-level Raydium representation of that position, including the pool id, tick + * boundaries and liquidity value decoded from the position account.
+ * + *This method does not calculate token amounts, price ranges or higher-level strategy values. + * Higher-level modules may use the returned state together with pool state, current price and CLMM + * math to calculate those values.
+ * + * @param positionNftId the Raydium concentrated liquidity position NFT id + * @return the raw Raydium concentrated position state for the supplied position NFT + * @throws IOException if the position state could not be fetched or decoded + * @throws InterruptedException if the calling thread is interrupted while fetching the position state + */ + RaydiumConcentratedPositionState fetchConcentratedPositionState( + ΩRaydiumLiquidityPoolPositionNftIdΩ positionNftId + ) throws IOException, InterruptedException; + + /** + * Fetches basic information about a Raydium concentrated liquidity pool. + * + *The returned information contains the pool id, token mint addresses, + * token decimals and the current pool price as reported by Raydium. This is + * low-level pool information that can be combined with concentrated position + * state to interpret ticks, price ranges and liquidity values.
+ * + *This method does not fetch an owner's position and does not calculate token + * amounts for a position.
+ * + * @param poolId the Raydium concentrated liquidity pool id + * @return basic information about the concentrated liquidity pool + * @throws IOException if the pool information could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the pool information + */ + RaydiumConcentratedPoolInfo fetchConcentratedPoolInfo( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Fetches the raw on-chain state for a Raydium concentrated liquidity pool. + * + *The returned state is decoded from the Raydium concentrated pool account on Solana. + * It contains low-level CLMM state such as active liquidity, the current square-root price + * in Q64.64 format and the current tick index.
+ * + *This method is different from {@link #fetchConcentratedPoolInfo(ΩRaydiumLiquidityPoolConcentratedIdΩ)}, + * which fetches pool information from Raydium's REST API. This method is intended for calculations + * that need fresher or more precise on-chain CLMM state than the REST API price field can provide.
+ * + *This method does not fetch token metadata, token decimals or position state. Those values + * must be supplied separately when needed for higher-level calculations.
+ * + * @param poolId the Raydium concentrated liquidity pool id + * @return the raw on-chain state for the concentrated liquidity pool + * @throws IOException if the pool state could not be fetched or decoded + * @throws InterruptedException if the calling thread is interrupted while fetching the pool state + */ + RaydiumConcentratedPoolState fetchConcentratedPoolState( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Calculates the price range represented by a Raydium concentrated liquidity position. + * + *The returned range is calculated from the position's lower and upper tick indexes, + * adjusted by the token decimals from the concentrated pool information.
+ * + *Raydium concentrated liquidity ranges are treated as lower-inclusive and + * upper-exclusive. This avoids overlapping ownership at shared boundaries between + * adjacent tick ranges.
+ * + * @param positionState the concentrated position state containing the lower and upper tick indexes + * @param poolInfo the concentrated pool information containing token decimals and pool context + * @param decimalPlaces the number decimal places in the resulting price currency + * @return the price range represented by the concentrated liquidity position + */ + Range<ΩPriceΩ> calculateConcentratedPositionPriceRange( + RaydiumConcentratedPositionState positionState, + RaydiumConcentratedPoolInfo poolInfo, + int decimalPlaces + ); + + /** + * Calculates the token amounts represented by a Raydium concentrated liquidity position using + * raw on-chain pool state for the current square-root price. + * + *The returned amounts are calculated from the position liquidity, the position tick range, + * token decimals from the concentrated pool information, and the current square-root price from + * the supplied on-chain pool state. This can be more precise than using the REST API price field + * from {@link RaydiumConcentratedPoolInfo}.
+ * + *If the current pool price is below the position range, the position is represented as + * token A. If the current pool price is above the position range, the position is represented + * as token B. If the current pool price is inside the position range, the position is + * represented as a mix of token A and token B.
+ * + *This method performs only the mathematical conversion from Raydium concentrated position + * state and pool state to token amounts. It does not fetch position state, pool information or + * pool state.
+ * + * @param positionState the concentrated position state containing tick indexes and liquidity + * @param poolInfo the concentrated pool information containing token mints and decimals + * @param poolState the on-chain concentrated pool state containing current square-root price and tick + * @return the token amounts represented by the concentrated liquidity position + */ + RaydiumLiquidityPoolTokenAmounts calculateConcentratedPositionTokenAmounts( + RaydiumConcentratedPositionState positionState, + RaydiumConcentratedPoolInfo poolInfo, + RaydiumConcentratedPoolState poolState + ); +} diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolInfo.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolInfo.tjava new file mode 100644 index 0000000..4f8683f --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolInfo.tjava @@ -0,0 +1,30 @@ +package com.r35157.libs.raydium; + +import java.math.BigDecimal; + +/** + * Represents basic information about a Raydium concentrated liquidity pool. + * + *This record contains the pool-level information needed to interpret + * concentrated position state. The mint addresses and decimals are required when + * converting Raydium tick indexes into human-readable prices.
+ * + *This record is intentionally a low-level Raydium model. It does not describe + * an owner's position and does not calculate token amounts for a position.
+ * + * @param poolId the Raydium concentrated liquidity pool id + * @param mintA the SPL mint address of token A + * @param mintADecimals the number of decimals used by token A + * @param mintB the SPL mint address of token B + * @param mintBDecimals the number of decimals used by token B + * @param priceEstimate the pool price estimate as reported by Raydium + */ +public record RaydiumConcentratedPoolInfo( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId, + ΩSPLMintAddressΩ mintA, + ΩamountDecimalsΩ mintADecimals, + ΩSPLMintAddressΩ mintB, + ΩamountDecimalsΩ mintBDecimals, + ΩPriceΩ priceEstimate +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolState.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolState.tjava new file mode 100644 index 0000000..97ebca3 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolState.tjava @@ -0,0 +1,32 @@ +package com.r35157.libs.raydium; + +import java.math.BigInteger; + +/** + * Represents raw on-chain state for a Raydium concentrated liquidity pool. + * + *This record contains low-level CLMM pool state decoded from the Raydium + * concentrated liquidity pool account on Solana. It is different from + * {@link RaydiumConcentratedPoolInfo}, which represents pool information fetched + * from Raydium's REST API.
+ * + *The {@code sqrtPriceX64} field represents the current square-root price in + * Raydium's fixed-point Q64.64 format. This value can be used for more precise + * concentrated liquidity calculations than using the REST API price field.
+ * + *This record does not contain token mint metadata, decimals or display prices. + * Those values belong in {@link RaydiumConcentratedPoolInfo} or in higher-level + * calculations that combine pool state with pool information.
+ * + * @param poolId the Raydium concentrated liquidity pool id + * @param liquidity the currently active liquidity in the pool + * @param sqrtPriceX64 the current square-root price in Q64.64 fixed-point format + * @param tickCurrent the current Raydium liquidity tick index + */ +public record RaydiumConcentratedPoolState( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId, + ΩRaydiumLiquidityΩ liquidity, + ΩRaydiumSqrtPriceX64Ω sqrtPriceX64, + ΩraydiumLiquidityTickIndexΩ tickCurrent +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPositionState.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPositionState.tjava new file mode 100644 index 0000000..b1f07aa --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPositionState.tjava @@ -0,0 +1,13 @@ +package com.r35157.libs.raydium; + +import java.math.BigDecimal; +import java.math.BigInteger; + +public record RaydiumConcentratedPositionState( + ΩRaydiumLiquidityPoolPositionNftIdΩ nftId, + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId, + ΩraydiumLiquidityTickIndexΩ tickLowerIndex, + ΩraydiumLiquidityTickIndexΩ tickUpperIndex, + ΩRaydiumLiquidityΩ liquidity +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPositionStandard.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPositionStandard.tjava new file mode 100644 index 0000000..2991e8f --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPositionStandard.tjava @@ -0,0 +1,29 @@ +package com.r35157.libs.raydium; + +import java.math.BigDecimal; + +/** + * Represents a standard Raydium liquidity pool position. + * + *A standard liquidity position is represented by ownership of liquidity pool + * tokens. The liquidity pool token mint identifies the LP token, while the SPL + * token account identifies where the owner holds the LP token balance.
+ * + *This record describes both the account holding the LP tokens and the amount + * of LP tokens held in that account. It does not represent a concentrated + * liquidity NFT position.
+ * + * @param positionId the Raydium liquidity pool position id + * @param lpMintId the SPL mint address of the Raydium liquidity pool token + * @param lpAccount the Raydium liquidity pool account associated with the position + * @param lpTokenAccount the SPL token account holding the liquidity pool tokens + * @param lpTokenAmount the amount of liquidity pool tokens held in the SPL token account + */ +public record RaydiumLiquidityPoolPositionStandard( + ΩRaydiumLiquidityPoolPositionIdΩ positionId, + ΩRaydiumLiquidityPoolPositionMintIdΩ lpMintId, + ΩRaydiumLiquidityPoolAccountΩ lpAccount, + ΩSPLTokenAccountΩ lpTokenAccount, + ΩAmountΩ lpTokenAmount +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPrice.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPrice.tjava new file mode 100644 index 0000000..24bd54d --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPrice.tjava @@ -0,0 +1,18 @@ +package com.r35157.libs.raydium; + +import com.r35157.libs.valuetypes.basic.MoneyAmount; + +/** + * Represents the price of a Raydium liquidity pool. + * + *The pool id identifies the Raydium liquidity pool for which the price applies. + * The amount contains the price value returned or calculated for that pool.
+ * + * @param poolId the Raydium liquidity pool id that the price belongs to + * @param amount the price amount for the liquidity pool + */ +public record RaydiumLiquidityPoolPrice( + ΩRaydiumLiquidityPoolIdΩ poolId, + MoneyAmount amount +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolTokenAmounts.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolTokenAmounts.tjava new file mode 100644 index 0000000..c229b1e --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolTokenAmounts.tjava @@ -0,0 +1,28 @@ +package com.r35157.libs.raydium; + +import java.math.BigDecimal; + +/** + * Represents token amounts for a Raydium liquidity pool or liquidity position. + * + *The pool id identifies the Raydium liquidity pool that the amounts belong to. + * The two mint addresses identify the tokens in the pool, and the corresponding + * amounts describe how much of each token is represented.
+ * + *This record can be used for total pool amounts as well as calculated position + * amounts, depending on the method returning it.
+ * + * @param poolId the Raydium liquidity pool id + * @param mintA the SPL mint address of token A + * @param amountA the amount of token A + * @param mintB the SPL mint address of token B + * @param amountB the amount of token B + */ +public record RaydiumLiquidityPoolTokenAmounts( + ΩRaydiumLiquidityPoolIdΩ poolId, + ΩSPLMintAddressΩ mintA, + ΩAmountΩ amountA, + ΩSPLMintAddressΩ mintB, + ΩAmountΩ amountB +) { +} \ No newline at end of file