SAP DOCv0.20.0
Best Practices

RPC and Network Configuration

Optimal RPC setup, Synapse Gateway, connection factories, rate limiting, and production strategies for SAP SDK v0.20.0.

RPC and Network Configuration

SDK Version: v0.20.0
Recommended RPC: Synapse Gateway (OOBE Protocol)

Reliable RPC connectivity is the foundation of any SAP integration. Every operation the SDK performs, whether reading an agent's identity or sending a transaction, goes through an RPC node. Choosing the right node, configuring retry logic, and understanding commitment levels directly affect the reliability and speed of your application.


What is an RPC Node?

For readers new to Solana: an RPC node is a server that provides access to the blockchain. When you call client.agent.fetch(), the SDK sends an HTTP request to an RPC node, which reads the blockchain and returns the data. When you call client.agent.register(), the SDK sends a signed transaction to the RPC node, which forwards it to the Solana network for processing.

Public RPC nodes (like api.devnet.solana.com) are free but rate-limited. For production applications, dedicated RPC providers like Synapse Gateway, Triton, or QuickNode offer higher throughput, lower latency, and better reliability. Synapse Gateway is the recommended endpoint for SAP workloads because it ships pre-tuned for the program's account-heavy read patterns.


Connection Factories

The SDK provides two connection factories, depending on whether you need signing capabilities or just read access.

Signing Connection

import { SapConnection } from "@oobe-protocol-labs/synapse-sap-sdk";
import { Keypair } from "@solana/web3.js";

const { client, anchorProvider } = SapConnection.fromKeypair(
  "https://us-1-mainnet.oobeprotocol.ai/rpc?api_key=YOUR_KEY",
  keypair,
  { commitment: "confirmed" }
);

Read-Only from Provider

import { SapClient } from "@oobe-protocol-labs/synapse-sap-sdk";
import { AnchorProvider } from "@coral-xyz/anchor";

const client = SapClient.from(AnchorProvider.env());

Cluster Selection

ClusterRPC URLUse Case
Devnethttps://api.devnet.solana.comDevelopment, testing
Mainnethttps://api.mainnet-beta.solana.comProduction (rate limited)
CustomProvider URLDedicated RPC nodes (recommended)
ProviderMainnet URLFeatures
Synapse Gatewayhttps://us-1-mainnet.oobeprotocol.ai/rpcSAP-optimized, region-aware
Tritonhttps://mainnet.rpc.titan.to/High throughput, low latency
QuickNodehttps://YOUR-ENDPOINT.quiknode.pro/Global coverage, good docs
Heliushttps://mainnet.helius-rpc.com/?api-key=YOUR_KEYDeveloper-friendly, good free tier

Avoid public Solana RPC for production. The public endpoints (api.mainnet-beta.solana.com) enforce strict rate limits and may reject SAP-specific queries.


Environment-Based Configuration

// .env.local
SYNAPSE_API_KEY=your-api-key
SYNAPSE_NETWORK=mainnet
SYNAPSE_REGION=us-east

// src/lib/env.ts
const config = {
  rpcUrl: process.env.SYNAPSE_NETWORK === "mainnet"
    ? `https://us-1-mainnet.oobeprotocol.ai/rpc?api_key=${process.env.SYNAPSE_API_KEY}`
    : "https://api.devnet.solana.com",
  commitment: "confirmed" as const,
};

// Use in client initialization
const client = SapClient.from(new AnchorProvider(
  new Connection(config.rpcUrl, config.commitment),
  wallet,
  { commitment: config.commitment }
));

Rate Limiting Strategies

Client-Side Throttling

const DELAY_MS = 100; // 10 requests per second

async function throttledFetch<T>(
  fetcher: () => Promise<T>,
): Promise<T> {
  await new Promise((resolve) => setTimeout(resolve, DELAY_MS));
  return fetcher();
}

// Usage
const agent = await throttledFetch(() => client.agent.fetch());

Batch Requests

Combine related reads to reduce roundtrips:

const [agent, stats, tools] = await Promise.all([
  client.agent.fetch(),
  client.agent.fetchStats(),
  client.tools.fetchAll(),
]);

Retry with Exponential Backoff

async function retryWithBackoff<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  baseDelay = 500,
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error: unknown) {
      if (attempt === maxRetries) throw error;
      
      const isRateLimit = 
        error instanceof Error && error.message.includes("429");
      const delay = isRateLimit 
        ? baseDelay * Math.pow(2, attempt) 
        : baseDelay;
      
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw new Error("Unreachable");
}

// Usage
const agent = await retryWithBackoff(() => client.agent.fetch());

Commitment Levels

Commitment levels determine how "confirmed" a transaction needs to be before the RPC node considers it done. This is a concept unique to blockchains that does not exist in traditional databases.

LevelFinalityLatencyRecommended For
processedOptimistic~400msRead-only queries, UI updates
confirmedSupermajority~1sDefault for writes, agent registration
finalizedMax finality~12sPayment settlement, escrow closing

What these mean in practice:

  • processed is like checking your bank balance on the app: it shows the latest state, but a recent transaction might still be reversed.
  • confirmed is like receiving a receipt: the transaction has been validated by 2/3 of validators.
  • finalized is like seeing the transaction on your monthly statement: it is permanent and irreversible.

Use confirmed as the default. Only escalate to finalized when verifying payment settlement or closing escrows where absolute certainty is required.


Dual-Connection Strategy (v0.20.0)

Some authenticated RPC providers reject WebSocket connections (returning HTTP 400). This breaks SPL token operations and real-time subscriptions. The SDK's createDualConnection() solves this by using your primary authenticated RPC for transactions and a public fallback for WebSocket subscriptions.

import { createDualConnection } from "@oobe-protocol-labs/synapse-sap-sdk";

const { primary, fallback } = createDualConnection({
  // Authenticated RPC for transactions and account reads
  primaryUrl: "https://us-1-mainnet.oobeprotocol.ai/rpc?api_key=YOUR_KEY",
  // Optional: explicit fallback URL (auto-detected from cluster if omitted)
  fallbackUrl: "https://api.mainnet-beta.solana.com",
});

// primary  → use for SapConnection / transaction signing
// fallback → use for WebSocket subscriptions, token accounts

When to use: If you see WebSocket 400 errors, or if your RPC provider requires API keys that are incompatible with WebSocket upgrades. The dual-connection strategy costs nothing extra - it simply routes subscription traffic to a public node.

findATA Utility

The SDK also provides findATA() for SPL Associated Token Account lookup, which uses the fallback connection automatically:

import { findATA } from "@oobe-protocol-labs/synapse-sap-sdk";

const ata = await findATA(walletPublicKey, tokenMint);
// Returns the ATA address, using fallback connection if primary fails

Production Checklist

  1. Use a dedicated RPC endpoint (not the public Solana endpoint)
  2. Set commitment: "confirmed" as the default
  3. Implement retry logic with exponential backoff
  4. Add 429 (rate limit) detection with longer delays
  5. Monitor RPC latency and switch providers if degraded
  6. Cache getAccountInfo responses where appropriate (static PDAs)
  7. Use getProgramAccounts with memcmp filters to reduce response size
  8. Keep WebSocket connections alive for real-time subscription use cases
  9. Configure fallback RPC for redundancy
  10. Test on devnet first before mainnet deployment

Troubleshooting

429 Too Many Requests

// Solution: Implement client-side throttling
const DELAY_MS = 200; // 5 requests per second

async function fetchWithThrottle<T>(fn: () => Promise<T>): Promise<T> {
  await new Promise(r => setTimeout(r, DELAY_MS));
  return fn();
}

WebSocket 400 Error

// Solution: Use dual-connection strategy
const { primary, fallback } = createDualConnection({
  primaryUrl: "https://YOUR_AUTHENTICATED_RPC",
  fallbackUrl: "https://api.mainnet-beta.solana.com",
});

High Latency

# Test RPC latency
curl -X POST -H "Content-Type: application/json" \
  --data '{"jsonrpc":"2.0","id":1,"method":"getHealth"}' \
  "https://YOUR_RPC_URL"

# Expected: < 100ms for regional endpoints

Next Steps


Last Updated: June 2026
SDK Version: 0.20.0