Soldev

Solana RPC

Last updated:

An RPC is our interface for communicating with on-chain data.

Typically you want to connect to both the http and websocket endpoints.

import {
  Rpc,
  SolanaRpcApi,
  SolanaRpcSubscriptionsApi,
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  testnet
} from "@solana/kit"

export type Client = {
  rpc: Rpc<SolanaRpcApi>
  rpcSubscriptions: RpcSubscriptions<SolanaRpcSubscriptionsApi>
}

export const createDefaultSolanaClient = (): Client => {
  const rpc = createSolanaRpc(testnet("http://127.0.0.1:8899"))
  const rpcSubscriptions = createSolanaRpcSubscriptions(testnet("ws://127.0.0.1:8900"))
  return { rpc, rpcSubscriptions }
}

For example when we listen to changes on a particular account we want to listen to a websocket to be immediately notified. When you want to fetch the current state you would use the http endpoint instead.

Each of these is a separate transport, one for websockets, one for http. There are other transports like GraphQL you can use as well.

Each transport is responsible for understanding its own capabilities depending on the type you pass in.

export type MainnetUrl = string & { '~cluster': 'mainnet' };
export type DevnetUrl = string & { '~cluster': 'devnet' };
export type TestnetUrl = string & { '~cluster': 'testnet' };
export type ClusterUrl = DevnetUrl | MainnetUrl | TestnetUrl | string;

Depending on how you wrap your endpoint, certain capabilities will be available to you or not. For example, because we used a testnet endpoint, we would have the capability to airdrop Solana. Something obviously not available on mainnet.

When we actually use the API we can call one of the Solana methods and send them to receive a response:

await rpc.getBalance(address('95DpK3y3GF7U8s1k4EvZ7xqyeCkhsHeZaE97iZpHUGMN')).send()

These are all interfaces over doing things manually. For example we can create our own http transport and send our own custom request:

import { createHttpTransport } from '@solana/rpc-transport-http';

const transport = createHttpTransport({ url: 'https://api.mainnet-beta.solana.com' });
const response = await transport({
    payload: { id: 1, jsonrpc: '2.0', method: 'getSlot' },
});
const data = await response.json();

Building transports can be a useful step in pooling connections to multiple endpoints:

import { RpcTransport } from '@solana/rpc-spec';
import { RpcResponse } from '@solana/rpc-spec-types';
import { createHttpTransport } from '@solana/rpc-transport-http';

// Create a transport for each RPC server
const transports = [
    createHttpTransport({ url: 'https://mainnet-beta.my-server-1.com' }),
    createHttpTransport({ url: 'https://mainnet-beta.my-server-2.com' }),
    createHttpTransport({ url: 'https://mainnet-beta.my-server-3.com' }),
];

// Create a wrapper transport that distributes requests to them
let nextTransport = 0;
async function roundRobinTransport<TResponse>(...args: Parameters<RpcTransport>): Promise<RpcResponse<TResponse>> {
    const transport = transports[nextTransport];
    nextTransport = (nextTransport + 1) % transports.length;
    return await transport(...args);
}