Monitoring Liquidity Pools on Orca
Monitoring and fetching details about liquidity pools is crucial for understanding their current state, whether you want to gather insights about a Splash Pool, a Concentrated Liquidity Pool, or all pools between specific token pairs.
1. Overview of Pool Monitoring
Fetching liquidity pool details helps developers gain insight into the current state of the pool, whether it is initialized or uninitialized, and retrieve relevant metrics like liquidity, price, and fee rates.
The SDKs offer three main functions to help developers monitor the pools:
- Fetch Splash Pool: Fetches the details of a specific Splash Pool.
- Fetch Concentrated Liquidity Pool: Fetches the details of a specific Concentrated Liquidity Pool.
- Fetch Pools: Fetches all possible liquidity pools between two token mints, with various tick spacings.
Initialized vs. Uninitialized Pools
Each token pair can have multiple pools based on different tick spacings, corresponding to various fee tiers. When fetching pool data, it's possible to request a pool with a tick spacing that hasn't been used to create a pool for the given token pair. In this case, you'll receive a pool object with default parameters and an indication that the pool has not been set up.
When fetching all pools for a token pair, which iterates through all possible tick spacings, both initialized and uninitialized pools can be returned, allowing you to identify pools that have not yet been created.
2. Getting Started Guide
- Rust
- TypeScript Kit
- TypeScript Legacy
Fetching a pool by Address
If you already have the address of a Whirlpool:
- Whirlpool Address: Provide the address of the specific Whirlpool you want to fetch.
- Fetch Pool Details: Use the function to fetch the details of the Whirlpool at the provided address.
use orca_whirlpools::{
fetch_whirlpool, set_whirlpools_config_address, WhirlpoolsConfigInput,
};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;
#[tokio::main]
async fn main() {
set_whirlpools_config_address(WhirlpoolsConfigInput::SolanaDevnet).unwrap();
let rpc = RpcClient::new("https://api.devnet.solana.com".to_string());
let whirlpool_address = Pubkey::from_str("3KBZiL2g8C7tiJ32hTv5v3KM7aK9htpqTw4cTXz1HvPt").unwrap(); // SOL/devUSDC
let whirlpool = fetch_whirlpool(&rpc, whirlpool_address).await.unwrap();
println!("Pool data: {:?}", whirlpool.data);
}
Fetching a Splash Pool by Token Pair
- Token Mint Addresses: Provide the mint addresses of the two tokens that make up the liquidity pool.
- Fetch Pool Details: Use the appropriate function to fetch the details of the specified Splash Pool.
use orca_whirlpools::{
fetch_splash_pool, set_whirlpools_config_address, PoolInfo, WhirlpoolsConfigInput,
};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;
async fn main() {
set_whirlpools_config_address(WhirlpoolsConfigInput::SolanaDevnet).unwrap();
let rpc = RpcClient::new("https://api.devnet.solana.com".to_string());
let token_a = Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap();
let token_b = Pubkey::from_str("BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k").unwrap(); // devUSDC
let pool_info = fetch_splash_pool(&rpc, token_a, token_b).await.unwrap();
match pool_info {
PoolInfo::Initialized(pool) => println!("Pool is initialized: {:?}", pool),
PoolInfo::Uninitialized(pool) => println!("Pool is not initialized: {:?}", pool),
}
}
Fetching a Concentrated Liquidity Pool by Token Pair
- Token Mint Addresses: Provide the mint addresses of the two tokens that make up the liquidity pool.
- Tick Spacing: Specify the tick spacing, which defines the intervals for price ticks.
- Fetch Pool Details: Use the appropriate function to fetch the details of the specified Concentrated Liquidity Pool.
use orca_whirlpools::{
fetch_concentrated_liquidity_pool, set_whirlpools_config_address, PoolInfo, WhirlpoolsConfigInput
};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;
#[tokio::main]
async fn main() {
set_whirlpools_config_address(WhirlpoolsConfigInput::SolanaDevnet).unwrap();
let rpc = RpcClient::new("https://api.devnet.solana.com".to_string());
let token_a = Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap();
let token_b = Pubkey::from_str("BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k").unwrap(); // devUSDC
let tick_spacing = 64;
let pool_info = fetch_concentrated_liquidity_pool(&rpc, token_a, token_b, tick_spacing).await.unwrap();
match pool_info {
PoolInfo::Initialized(pool) => println!("Pool is initialized: {:?}", pool),
PoolInfo::Uninitialized(pool) => println!("Pool is not initialized: {:?}", pool),
}
}
Fetching Pools by Token Pairs
- Token Mint Addresses: Provide the mint addresses of the two tokens that make up the liquidity pool.
- Fetch Pool Details: Use the appropriate function to fetch the details of all pools for the specified token pair.
use orca_whirlpools::{
fetch_whirlpools_by_token_pair, set_whirlpools_config_address, PoolInfo, WhirlpoolsConfigInput,
};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;
#[tokio::main]
async fn main() {
set_whirlpools_config_address(WhirlpoolsConfigInput::SolanaDevnet).unwrap();
let rpc = RpcClient::new("https://api.devnet.solana.com".to_string());
let token_a = Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap();
let token_b = Pubkey::from_str("BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k").unwrap(); // devUSDC
let pool_infos = fetch_whirlpools_by_token_pair(&rpc, token_a, token_b)
.await
.unwrap();
for pool_info in pool_infos {
match pool_info {
PoolInfo::Initialized(pool) => println!("Pool is initialized: {:?}", pool),
PoolInfo::Uninitialized(pool) => println!("Pool is not initialized: {:?}", pool),
}
}
}
Fetching a pool by Address
If you already have the address of a Whirlpool:
- Whirlpool Address: Provide the address of the specific Whirlpool you want to fetch.
- Fetch Pool Details: Use the function to fetch the details of the Whirlpool at the provided address.
import { fetchWhirlpool, setWhirlpoolsConfig } from '@orca-so/whirlpools';
import { createSolanaRpc, devnet, address } from '@solana/kit';
await setWhirlpoolsConfig('solanaDevnet');
const devnetRpc = createSolanaRpc(devnet('https://api.devnet.solana.com'));
const whirlpoolAddress = address("3KBZiL2g8C7tiJ32hTv5v3KM7aK9htpqTw4cTXz1HvPt"); // SOL/devUSDC
const whirlpool = await fetchWhirlpool(
devnetRpc,
whirlpoolAddress
);
console.log("Pool data:", whirlpool.data);
Fetching a Splash Pool by Token Pair
- Token Mint Addresses: Provide the mint addresses of the two tokens that make up the liquidity pool.
- Fetch Pool Details: Use the appropriate function to fetch the details of the specified Splash Pool.
import { fetchSplashPool, setWhirlpoolsConfig } from '@orca-so/whirlpools';
import { createSolanaRpc, devnet, address } from '@solana/kit';
await setWhirlpoolsConfig('solanaDevnet');
const devnetRpc = createSolanaRpc(devnet('https://api.devnet.solana.com'));
const tokenMintOne = address("So11111111111111111111111111111111111111112");
const tokenMintTwo = address("BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k"); //devUSDC
const poolInfo = await fetchSplashPool(
devnetRpc,
tokenMintOne,
tokenMintTwo
);
if (poolInfo.initialized) {
console.log("Pool is initialized:", poolInfo);
} else {
console.log("Pool is not initialized:", poolInfo);
};
Fetching a Concentrated Liquidity Pool by Token Pair
- Token Mint Addresses: Provide the mint addresses of the two tokens that make up the liquidity pool.
- Tick Spacing: Specify the tick spacing, which defines the intervals for price ticks.
- Fetch Pool Details: Use the appropriate function to fetch the details of the specified Concentrated Liquidity Pool.
import { fetchConcentratedLiquidityPool, setWhirlpoolsConfig } from '@orca-so/whirlpools';
import { createSolanaRpc, devnet, address } from '@solana/kit';
await setWhirlpoolsConfig('solanaDevnet');
const devnetRpc = createSolanaRpc(devnet('https://api.devnet.solana.com'));
const tokenMintOne = address("So11111111111111111111111111111111111111112");
const tokenMintTwo = address("BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k");
const tickSpacing = 64;
const poolInfo = await fetchConcentratedLiquidityPool(
devnetRpc,
tokenMintOne,
tokenMintTwo,
tickSpacing
);
if (poolInfo.initialized) {
console.log("Pool is initialized:", poolInfo);
} else {
console.log("Pool is not initialized:", poolInfo);
};
Fetching Pools by Token Pairs
- Token Mint Addresses: Provide the mint addresses of the two tokens that make up the liquidity pool.
- Fetch Pool Details: Use the appropriate function to fetch the details of all pools for the specified token pair.
import { fetchWhirlpoolsByTokenPair, setWhirlpoolsConfig } from '@orca-so/whirlpools';
import { createSolanaRpc, devnet, address } from '@solana/kit';
await setWhirlpoolsConfig('solanaDevnet');
const devnetRpc = createSolanaRpc(devnet('https://api.devnet.solana.com'));
const tokenMintOne = address("So11111111111111111111111111111111111111112");
const tokenMintTwo = address("BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k");
const poolInfos = await fetchWhirlpoolsByTokenPair(
devnetRpc,
tokenMintOne,
tokenMintTwo
);
poolInfos.forEach((poolInfo) => {
if (poolInfo.initialized) {
console.log("Pool is initialized:", poolInfo);
} else {
console.log("Pool is not initialized:", poolInfo);
}
});
Reading Whirlpool Accounts
The SDK provides the following methods to fetch and parse data from Whirlpool accounts on-chain.
Fetching Accounts
The TypeScript SDK has types setup to help you parse the corresponding accounts on-chain.
1. Account Fetcher
Use the AccountFetcher class's get functions to fetch and parse the Whirlpool account that you need. Note that this class also provides caching options.
const fetcher = new WhirlpoolAccountFetcher(connection);
const config: WhirlpoolsConfigData = await fetcher.getConfig(CONFIG_PUBLIC_KEY);
const poolAddress = PDAUtil.getPool(...);
const pool: WhirlpoolData = await fetcher.getPool(poolAddress);
2. Parsing fetched AccountInfo data
If you already have the Buffer from fetching the AccountInfo, use the Parsables classes (eg. ParsableWhirlpool) in the SDK to parse account buffer data into readable types.
const whirlpoolAccountInfo: Buffer = ...
const whirlpool: WhirlpoolData = ParsableWhirlpool.parse(accountInfoData)
Whirlpool Client
If you are already using WhirlpoolClient, you can fetch the data from the Whirlpool
or Position
class directly.
const context = new WhirlpoolContext(...);
const fetcher = new AccountFetcher(context.provider.connection);
const client = buildWhirlpoolClient(context, fetcher);
const pool = await client.getPool(poolAddress);
const position = await client.getPosition(positionAddress);
const poolData: WhirlpoolData = pool.getData();
const positionData: PositionData = position.getData();
// Perform Action...
const newestData = pool.refreshData();
Deriving Account Addresses
Almost all Whirlpools accounts are Program Derived Addresses. Use the PDAUtil class to derive the required addresses to access on-chain accounts.
3. Using Pool Data
After fetching pool information, you can use it to:
- Check if Pool Exists: Determine if a pool for a specific token pair and tick spacing has been created.
- Monitor Liquidity: Track the amount of liquidity in the pool over time.
- Track Prices: Monitor the current price of tokens in the pool.
- Calculate Fees: Calculate expected fees based on the pool's fee rate and volume.
- Data Analytics: Build analytics dashboards tracking pool performance and metrics.
4. Best Practices
When monitoring pools, consider these best practices:
- Caching: Implement caching to reduce RPC calls, especially for frequently accessed pools.
- Error Handling: Properly handle cases where pools might not exist.
- Batch Requests: When possible, batch your requests to reduce the number of RPC calls.
- Rate Limiting: Be mindful of RPC rate limits when monitoring multiple pools.
- Data Freshness: Determine how recent the data needs to be for your application.