# 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

```typescript
// ❌ 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

```typescript
// 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

```typescript
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

```typescript
// ❌ 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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```typescript
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

```typescript
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

```typescript
// 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

```typescript
// 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

```typescript
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

```typescript
// 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

1. **Batch when possible** - Reduce transaction count
2. **Cache frequently used data** - Minimize RPC calls
3. **Validate early** - Fail fast with clear errors
4. **Handle errors gracefully** - Implement retries
5. **Monitor performance** - Track and optimize
6. **Use appropriate data structures** - Maps for lookups
7. **Clean up resources** - Prevent memory leaks


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ferra.ag/integration/dlmm/typescript-sdk/utilities-and-reference/best-practices.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
