Best Practices
Performance optimization tips for efficient DLMM interactions. Minimize gas usage, batch operations effectively, and implement robust patterns for production applications.
Gas Optimization
Batch Operations
// ❌ Bad: Multiple transactions
await sdk.Pair.removeLiquidity(pair, { positionId, binIds: [1] });
await sdk.Pair.removeLiquidity(pair, { positionId, binIds: [2] });
await sdk.Pair.removeLiquidity(pair, { positionId, binIds: [3] });
// ✅ Good: Single transaction
await sdk.Pair.removeLiquidity(pair, {
positionId,
binIds: [1, 2, 3]
});
Minimize Bin Count
// Gas scales with number of bins
const gasEfficient = {
// ❌ Excessive: 100 bins
wide: Array.from({length: 101}, (_, i) => i - 50),
// ✅ Efficient: Focused liquidity
focused: Array.from({length: 21}, (_, i) => i - 10)
};
// Consider gas cost vs capital efficiency
function optimizeBinCount(expectedVolatility: number): number {
if (expectedVolatility < 0.01) return 10; // Stable
if (expectedVolatility < 0.05) return 20; // Normal
return 30; // Volatile
}
Efficient Data Fetching
Cache Pair Data
class PairCache {
private cache = new Map<string, { data: LBPair, timestamp: number }>();
private ttl = 60000; // 1 minute
async getPair(pairAddress: string): Promise<LBPair> {
const cached = this.cache.get(pairAddress);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const pair = await sdk.Pair.getPair(pairAddress);
if (pair) {
this.cache.set(pairAddress, { data: pair, timestamp: Date.now() });
}
return pair;
}
}
Batch RPC Calls
// ❌ Bad: Sequential calls
const pairs = [];
for (const address of pairAddresses) {
const pair = await sdk.Pair.getPair(address);
pairs.push(pair);
}
// ✅ Good: Parallel calls
const pairs = await Promise.all(
pairAddresses.map(address => sdk.Pair.getPair(address))
);
Selective Data Loading
// Only fetch what you need
async function getPositionSummary(positionId: string) {
// Don't fetch full bin details if only need count
const position = await sdk.Position.getLbPosition(positionId);
// Lazy load expensive data
let bins = null;
const getBins = async () => {
if (!bins) {
const pair = await sdk.Pair.getPair(position.pair_id);
bins = await sdk.Position.getPositionBins(pair, positionId);
}
return bins;
};
return { position, getBins };
}
Smart Contract Interactions
Reuse Transactions
// Build transaction once, execute multiple times
const buildAddLiquidityTx = (params: AddLiquidityParams) => {
const tx = new Transaction();
// ... build transaction
return tx;
};
// Reuse for similar operations
const template = buildAddLiquidityTx(baseParams);
Optimize Call Data
// Minimize distribution arrays
function compressDistribution(
distribution: number[]
): { indices: number[], values: number[] } {
const indices = [];
const values = [];
distribution.forEach((value, index) => {
if (value > 0) {
indices.push(index);
values.push(value);
}
});
return { indices, values };
}
Error Handling Patterns
Retry with Backoff
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error;
// Exponential backoff
const delay = Math.min(1000 * Math.pow(2, i), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
Circuit Breaker
class CircuitBreaker {
private failures = 0;
private lastFailTime = 0;
private threshold = 5;
private timeout = 60000; // 1 minute
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.isOpen()) {
throw new Error("Circuit breaker is open");
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private isOpen(): boolean {
return this.failures >= this.threshold &&
Date.now() - this.lastFailTime < this.timeout;
}
private onSuccess() {
this.failures = 0;
}
private onFailure() {
this.failures++;
this.lastFailTime = Date.now();
}
}
Memory Management
Clean Up Large Objects
// Release memory after use
async function processLargeDataset(pair: LBPair) {
let reserves = await sdk.Pair.getPairReserves(pair);
// Process data
const result = processReserves(reserves);
// Clear reference
reserves = null;
return result;
}
Stream Large Results
// Process in chunks for large datasets
async function* streamPositionBins(
pair: LBPair,
positionId: string,
chunkSize: number = 50
) {
const bins = await sdk.Position.getPositionBins(pair, positionId);
for (let i = 0; i < bins.length; i += chunkSize) {
yield bins.slice(i, i + chunkSize);
}
}
// Usage
for await (const chunk of streamPositionBins(pair, positionId)) {
processChunk(chunk);
}
Production Checklist
Pre-deployment
const productionChecks = {
// 1. Input validation
validateInputs: (params: any) => {
if (!params.amount || params.amount <= 0n) {
throw new Error("Invalid amount");
}
},
// 2. Network check
ensureMainnet: () => {
if (sdk.network !== "mainnet") {
throw new Error("Not on mainnet");
}
},
// 3. Gas buffer
addGasBuffer: (estimatedGas: bigint) => {
return (estimatedGas * 120n) / 100n; // 20% buffer
},
// 4. Slippage protection
defaultSlippage: 0.5, // 0.5%
maxSlippage: 5.0 // 5%
};
Performance Monitoring
// Track operation performance
class PerformanceMonitor {
private metrics = new Map<string, number[]>();
async measure<T>(
name: string,
fn: () => Promise<T>
): Promise<T> {
const start = performance.now();
try {
return await fn();
} finally {
const duration = performance.now() - start;
if (!this.metrics.has(name)) {
this.metrics.set(name, []);
}
this.metrics.get(name)!.push(duration);
// Log slow operations
if (duration > 5000) {
console.warn(`Slow operation: ${name} took ${duration}ms`);
}
}
}
getStats(name: string) {
const times = this.metrics.get(name) || [];
return {
avg: times.reduce((a, b) => a + b, 0) / times.length,
min: Math.min(...times),
max: Math.max(...times)
};
}
}
Key Principles
Batch when possible - Reduce transaction count
Cache frequently used data - Minimize RPC calls
Validate early - Fail fast with clear errors
Handle errors gracefully - Implement retries
Monitor performance - Track and optimize
Use appropriate data structures - Maps for lookups
Clean up resources - Prevent memory leaks
Last updated