Understanding Solana RPC: A Comprehensive Guide for Developers

This article provides a high-level overview of Solana RPC, focusing on key concepts and tools that developers need to get started.

Understanding Solana RPC: A Comprehensive Guide for Developers

As blockchain technology continues to evolve, Solana has emerged as a leading platform for building scalable and high-performance decentralized applications (DApps). Understanding the Solana Remote Procedure Call (RPC) is crucial for developers looking to build on Solana. This article provides a high-level overview of Solana RPC, focusing on key concepts and tools that developers need to get started.

Core Concepts

What is RPC?

Remote Procedure Call is a protocol that enables a program to execute a procedure on a different address space, typically on another computer within a shared network. In the context of blockchain, RPC facilitates communication between different components of the network, allowing for transactions, data retrieval, and other network operations. This mechanism is essential for client-server interactions, where the client sends a request to the server, which processes the request and returns a response.

Why does Solana use RPC?

Solana uses RPC to facilitate interaction between decentralized applications (DApps) and the blockchain. Direct communication with the decentralized nodes in the network requires a standardized, efficient, and secure protocol. RPC nodes serve this purpose by enabling clients (like DApps or wallets) to send commands to the Solana nodes over the network. Through RPC, developers can perform a wide range of actions, such as querying account balances, sending transactions, or fetching blockchain data, without needing to understand the low-level workings of the Solana network.

What is JSON-RPC?

Solana RPC nodes accept HTTP requests using the JSON-RPC 2.0 specification. This API allows developers to interact with the Solana blockchain by sending HTTP POST requests with JSON-formatted data. The JSON request typically includes the following fields:

  • jsonrpc: A string specifying the version of the JSON-RPC protocol. For Solana, this is always set to 2.0.
  • id: A unique identifier for the request, which can be a number, string, or null. This helps in matching responses with requests.
  • method: A string containing the name of the method to be invoked.
  • params: An array or object containing the parameters to be passed to the method.

For example, a request to get the balance of an account might look like this:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getBalance",
  "params": [
    "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"
  ]
}

The response will be a JSON object containing the requested data or a success confirmation. For instance, a successful response to the above request might be:

{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "slot": 65340475
    },
    "value": 1000000
  },
  "id": 1
}

The above result is fairly simple, but Solana RPC methods can also return complex responses structured as JSON objects, each containing specific keyed values. Common data structures include:

  • Transactions: detailed with elements like signatures, a message object containing account keys, a header, and a list of program instructions, among others
  • Inner Instructions: provide transparency by recording cross-program instructions invoked during transaction processing, organized by their originating transaction instruction
  • Token Balances: include information such as the account index, mint, owner, and the token amount, represented in both raw and UI-adjusted formats

Understanding each field in the request and result is important for building applications that can communicate seamlessly with Solana. Developers should reference the official Solana documentation to learn about each method in terms of its parameters and response structure.

HTTP vs WebSocket Methods

The Solana RPC API includes many different RPC methods, all of which use either the HTTP-based (request and response) protocol or the WebSocket (subscription) protocol:

The HTTP and WebSocket protocols are each suitable for different methods of reading data from the blockchain.

  • HTTP is well-suited for one-off requests where a client needs to retrieve specific data or execute a transaction. For instance, if a developer wants to simply query an account's balance or send a transaction, an HTTP call is ideal. The request-response nature is straightforward and easy to implement. Examples of HTTP methods:
    • getBalance: Retrieves the balance of a specified account.
    • sendTransaction: Submits a signed transaction to the network for processing.
    • getAccountInfo: Fetches detailed information about a specific account, including its data and state.

The full list of Solana HTTP methods can be found in the official Solana documentation.

  • WebSocket subscriptions are useful for scenarios requiring real-time updates to data and event-driven interactions. WebSocket connections maintain a persistent, two-way communication channel between the client and server, where the server pushes data to the client as soon as it’s available. This protocol is useful in applications like trading platforms, where functions are event-driven based on account changes. Examples of WebSocket methods:
    • accountSubscribe: subscribes to changes in a specific account, sending updates whenever the account data is modified.
    • logsSubscribe: subscribes to logs from transactions, allowing clients to receive log updates as they are generated on-chain.

The full list of Solana WebSocket methods can be found in the official Solana documentation.

Request Parameters and Response

When interacting with Solana through RPC, developers will frequently work with some key parameters that greatly influence how data is retrieved and processed. In addition, most RPC methods will include other method-specific parameters necessary for the particular operation being performed.

Let’s explore some of the most important common types of parameters, which will give you more control and better data handling in your requests.

Commitment Levels

State commitment in Solana refers to the extent to which a particular state of the blockchain is considered final. When using RPC methods that read the state, you can specify one of three commitment levels: finalized, confirmed, or processed.

The different commitment levels allow developers to balance the trade-off between more recent and certain data. Lower levels like processed allow access to the latest state changes but carry a risk of potential rollbacks, while higher levels like finalized provide stronger assurances that the state will not change.

Below, we briefly describe each of the three commitment levels.

  • The processed commitment level means the node queries its most recent block, representing the latest state right after a transaction is processed. This commitment level provides the most current state data. However, it comes with the caveat that this state may still be part of a fork and can potentially be rolled back. Around 5% of blocks fall into this category, making it suitable for read operations where accessing the latest available state is crucial, and occasional rollbacks can be tolerated.
  • The confirmed commitment level involves the node querying the most recent block that has been voted on by a majority of the cluster's validators but has not yet reached finality. This level balances recent state changes with a higher level of certainty. It is typically only a couple of seconds behind the processed state and has a very low chance of being rolled back. Although more stable than processed, confirmed states are still susceptible to rollbacks, albeit rarely.
  • The finalized commitment level means the node queries the most recent block that has been confirmed by a supermajority (â…” of votes) of the cluster and has reached maximum lockout, ensuring the block will not be reorganized. This level provides the highest assurance that the state will not be rolled back. However, waiting for the latest changes to reach this level takes longer compared to the processed and confirmed levels due to the extensive validation required. Under normal conditions, there is typically at least a 32-slot difference (about 13 seconds) between the most recent confirmed block and the most recent finalized block.

Example of a getAccountInfo request with finalized commitment level:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getAccountInfo",
  "params": [
    "MJKqp326RZCHnAAbew9MDdui3iCKWco7fsK9sVuZTX2",
    {
      "commitment": "finalized"
    }
  ]
}

For most use cases, the confirmed level offers a balanced approach by providing a reliable state with minimal latency, whereas the finalized level is advisable for operations where absolute certainty is critical.

For more information about commitment levels, refer to the official Solana documentation.

RpcResponse Structure

The RpcResponse structure is a core element of Solana RPC interactions, consisting of two main parts: context and value. The context includes the slot number where the operation was evaluated, providing a snapshot of the blockchain's state. The value part contains the actual data returned by the operation. This structure is designed to give developers both the data they need and the necessary context to understand that data.

Example of a getAccountInfo response structure:

{
    "jsonrpc": "2.0",
    "result": {
        "context": {
            "apiVersion": "1.18.22",
            "slot": 293526550
        },
        "value": {
            "data": "",
            "executable": false,
            "lamports": 598723268,
            "owner": "11111111111111111111111111111111",
            "rentEpoch": 18446744073709551615,
            "space": 0
        }
    },
    "id": 1
}

Parsed Responses

Parsed responses in Solana RPC allow developers to receive account or instruction data in a human-readable JSON format, simplifying the development process. By specifying "encoding":"jsonParsed", developers can work with data that is easier to understand and integrate into applications. Various native and SPL programs support JSON parsed format.

Examples of getTokenAccountsByOwner using base64 vs jsonParsed encoding:

base64 Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenAccountsByOwner",
  "params": [
    "MJKqp326RZCHnAAbew9MDdui3iCKWco7fsK9sVuZTX2",
    {
      "mint": "hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux"
    },
    {
      "encoding": "base64"
    }
  ]
}

base64 Response:

{
    "jsonrpc": "2.0",
    "result": {
        "context": {
            "apiVersion": "1.18.22",
            "slot": 293526928
        },
        "value": [
            {
                "account": {
                    "data": [
                        "CnMgk5GFYffdf8vsSr2FE97KGpZ/etejnWO0HtiTgIsFMzSY1a6krgCVhcQ/e4ww345wGH1KcT0TT5d/yN/gtQDEGScAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
                        "base64"
                    ],
                    "executable": false,
                    "lamports": 2039280,
                    "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                    "rentEpoch": 18446744073709551615,
                    "space": 165
                },
                "pubkey": "FSMJ7FwPp3JPUkjFNefTgikTWUsgMJw7t8dvZFP2Ve81"
            }
        ]
    },
    "id": 1
}

jsonParsed Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTokenAccountsByOwner",
  "params": [
    "MJKqp326RZCHnAAbew9MDdui3iCKWco7fsK9sVuZTX2",
    {
      "mint": "hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux"
    },
    {
      "encoding": "jsonParsed"
    }
  ]
}

jsonParsed Response:

{
    "jsonrpc": "2.0",
    "result": {
        "context": {
            "apiVersion": "1.18.22",
            "slot": 293527144
        },
        "value": [
            {
                "account": {
                    "data": {
                        "parsed": {
                            "info": {
                                "isNative": false,
                                "mint": "hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux",
                                "owner": "MJKqp326RZCHnAAbew9MDdui3iCKWco7fsK9sVuZTX2",
                                "state": "initialized",
                                "tokenAmount": {
                                    "amount": "656000000",
                                    "decimals": 8,
                                    "uiAmount": 6.56,
                                    "uiAmountString": "6.56"
                                }
                            },
                            "type": "account"
                        },
                        "program": "spl-token",
                        "space": 165
                    },
                    "executable": false,
                    "lamports": 2039280,
                    "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
                    "rentEpoch": 18446744073709551615,
                    "space": 165
                },
                "pubkey": "FSMJ7FwPp3JPUkjFNefTgikTWUsgMJw7t8dvZFP2Ve81"
            }
        ]
    },
    "id": 1
}

Filter Criteria

Filter criteria enhance the efficiency of data retrieval in Solana RPC by allowing developers to pre-filter the data returned in RpcResponse objects. Filters like memcmp and dataSize enable targeted searches within program account data, reducing the amount of irrelevant data that needs to be processed. This parameter is especially valuable in scenarios where developers need to query large datasets but only require specific subsets of that data.

Example of a getProgramAccounts request using filter criteria to find accounts containing the specified bytes:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getProgramAccounts",
  "params": [
    "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T",
    {
      "filters": [
        {
          "dataSize": 17
        },
        {
          "memcmp": {
            "offset": 4,
            "bytes": "3Mc6vR"
          }
        }
      ]
    }
  ]
}

You can learn more about filtering program data from these guides:

Errors

When working with Solana's JSON-RPC API,  it is also important to understand the meanings of various error codes and how to handle them.

The various RPC error codes that developers should expect to handle can classified into three general categories: standard JSON-RPC error codes, Solana error codes, and error codes specifically defined by the RPC provider.

  • Standard JSON-RPC Error Codes: the error codes from and including -32768 to -32000 are reserved for pre-defined errors within the JSON-RPC 2.0 Specification. For example:
    • -32601: Method not found (the requested method does not exist)
    • -32602: Invalid params (the requested parameter(s) are invalid for the method)

The list of standard JSON-RPC error codes can be found in the JSON RPC Specification documentation.

  • Solana JSON-RPC Error Codes: within the standard error code range, the subset of error codes between -32000 to -32099 are reserved for implementation-defined server errors. For example:
    • -32004: a requested block is not available on the node, possibly because it has been pruned from the node's storage
    • -32005: the Solana node is currently in an unhealthy state and unable to process requests properly

The list of Solana-specific JSON-RPC error codes can be found in the Solana validator repository.

  • Provider JSON-RPC Error Codes: RPC service providers can implement other error codes. For example:
    • 7700: Invalid Headers (headers sent with the request were invalid)
    • 7429: Rate Limited (rate limit for a particular method, namespace, etc., has been exceeded)

To find these, you should consult the documentation of your specific RPC provider. Our documentation contains custom error codes used by Syndica.

Furthermore, developers should also be aware of the HTTP and WebSocket protocols and potential errors, as the Solana JSON-RPC API uses these protocols to transfer the request and response data structures. Our documentation provides the different HTTP error codes used by Syndica.

Public vs Private RPC Endpoints

The Solana Foundation and other groups in the Solana ecosystem offer public RPC endpoints that are accessible to everyone and suitable for straightforward testing and basic connectivity. They are subject to low rate limits and offer only the standard set of RPC methods. Public RPC endpoints are suitable for educational purposes and low-volume use cases, but due to their shared nature, they often experience congestion and performance issues.

For production-grade applications, a private RPC endpoint with a reliable RPC provider is necessary. RPC providers such as Syndica offer an elastic-node architecture that automatically scales RPC nodes and load-balances requests among those nodes.

With Syndica, developers also gain access to a wide range of features to accelerate development, including:

  • Custom APIs: Syndica's ChainStream API can stream Solana transaction, slot, and block updates directly from the validator. This allows for processing a wide range of real-time on-chain events beyond what the standard Solana WebSocket methods offer.
  • App Deployments: Syndica simplifies the process of deploying DApps. Developers can connect their GitHub accounts and select repositories, and Syndica handles the deployment, making it easier to get DApps live.

Monitoring and Analytics: Syndica offers a monitoring dashboard that provides insights into all RPC calls made by your DApp. You can search, filter, and analyze these calls to debug and optimize performance.

Developer Resources

Solana RPC Documentation: Official guide for using Solana's RPC methods to interact with the blockchain - https://solana.com/docs/rpc.

JSON-RPC 2.0 Specification: Defines the standard protocol for JSON-RPC communication used by Solana RPC - https://www.jsonrpc.org/specification.

Solana Public RPC Endpoints: Lists available RPC endpoints for accessing Solana's public clusters - https://solana.com/docs/core/clusters.

Solana Stack Exchange: A community-driven Q&A platform for Solana developers and users - https://solana.stackexchange.com/.

Client Libraries: Language-specific SDKs for interacting with the Solana blockchain across various programming languages.

Language

SDK

Rust

solana_sdk

Typescript

@solana/web3.js

Python

solders

Java

solanaj

C++

solcpp

Go

solana-go

Kotlin

solanaKT

Dart

solana

Conclusion

As we've explored throughout this guide, Remote Procedure Call (RPC) is not just a technical detail in Solana development—it's the critical interface that bridges your applications with the Solana blockchain. As you continue your journey in Solana development, we strongly encourage you to dive deep into Solana's official documentation. It's an invaluable resource, offering comprehensive insights and best practices that go well beyond what we've covered here.

Also, stay engaged with the Solana developer community. The ecosystem is rapidly evolving, and keeping abreast of new developments, tools, and methodologies is crucial.

Experiment and iterate. The best way to truly understand RPC's capabilities and limitations is through hands-on experience.

–

Syndica empowers principal enterprises and architects advancing the Solana ecosystem. In building the Cloud of Web3, we provide best-in-class RPC node infrastructure, Developer APIs, a robust end-to-end platform, and the next cutting-edge Solana client validator, Sig, written in Zig. Our team offers the dedicated support, reliability, and user-focused orientation for seamless integration with Solana, allowing you to focus solely on building your enterprise.

Get started quickly today for free at syndica.io, and check out our documentation here.