Provider Best Practices

Recommendations

The following best practices apply to providers. Adopting these patterns consistently ensures that your applications remain reliable, performant, and maintainable as they scale.

Single Provider Instance

Reuse provider instances rather than creating new ones for each operation.

typescript
Reuse Provider
// Good: Reuse provider
const provider = new JSONRpcProvider({
    url: url, 
    network: network
});
const block1 = await provider.getBlock(1);
const block2 = await provider.getBlock(2);

// Bad: Creating new provider each time
const block1 = await new JSONRpcProvider({
    url: url, 
    network: network
}).getBlock(1);
const block2 = await new JSONRpcProvider({
    url: url, 
    network: network
}).getBlock(2);

Choose the Right Provider

Use JSON-RPC provider for most use cases and WebSocket provider only when real-time updates are required.

Batch Related Operations

When querying multiple values from the same provider, batch the calls into a single operation wherever the API supports it. Methods like getBalances() accept arrays and return results in one round-trip, significantly reducing latency and RPC load compared to issuing individual requests in a loop. For multiple distinct method calls on the same contract instance, use Promise.all() to execute them concurrently instead of awaiting each one sequentially.

typescript
Batching Requests
// Batch multiple independent requests
const [block, balance, utxos] = await Promise.all([
    provider.getBlockNumber(),
    provider.getBalance('bc1q...'),
    provider.utxoManager.getUTXOs({ address: 'bc1q...' }),
]);

Consider Cache Timing

The provider caches responses to reduce redundant network requests. For time-sensitive operations such as checking balances or UTXO availability before a transaction, ensure you are working with fresh data by bypassing or invalidating the cache.

typescript
Using Cache
// Good: Reuse for efficiency
const provider = new JSONRpcProvider({ 
    url: url, 
    network: network 
});
await provider.gasParameters();
await provider.gasParameters();  // Cached

// When fresh data is critical
const freshProvider = new JSONRpcProvider({ 
    url: url, 
    network: network 
});
const freshGas = await freshProvider.gasParameters();

Monitor Memory

Long-running applications may accumulate cache entries.

Connection Cleanup

Always ensure providers are properly disposed when no longer required.

  • Use the close() method for JSONRpcProvider.
  • Use the disconnect() method for WebSocketRpcProvider.
typescript
Disconnecting Provider
// Good: Cleanup

// For JSONRpcProvider
try{
    await provider.getBlock(1);
} finally{
    await provider.close();
}

// For WebSocketRpcProvider
try{
    await provider.getBlock(1);
} finally{
    await provider.disconnect();
}

Network Matching

Ensure the provider network matches your contract addresses.

Timeout Configuration

Select an appropriate timeouts for your use case.

typescript
Provider-wide Timeout
const provider = new JSONRpcProvider({
    url: url,
    network: network,
    timeout: 60000  // 60 second timeout
});

Implement Retries

Use exponential backoff for transient failures.

Rate Limit

Don't overwhelm nodes with requests.

Monitor Performance

Track request times and failure rates.

Enable Threaded Parsing

For production with large responses.