Interact with the Solana blockchain from the Internet Computer.
- Features
- Usage
- Deployment
- Limitations
- Supported Methods
- Supported Solana JSON-RPC Providers
- Reproducible build
- Learn More
- Related projects
- Contributing
- Releasing
- License
- No single point of failure: Each request will by default query 3 distinct Solana JSON-RPC providers and aggregate their results.
- Configurable consensus strategy: Choose how responses from multiple providers are aggregated depending on the needs of your application, e.g., 3-out-of-5 meaning that 5 providers will be queried and the overall response will be successful if at least 3 do agree (equality).
- Pay directly in cycles: No need to take care of API keys, each request can be paid for by attaching cycles.
- Bring your own:
- A Solana RPC method is not supported? There is an endpoint (
jsonRequest) to send any JSON-RPC request. - Missing your favorite Solana JSON-RPC provider? You can specify your own providers (
RpcSources::Custom).
- A Solana RPC method is not supported? There is an endpoint (
The SOL RPC canister runs on the fiduciary subnet with the following principal: tghme-zyaaa-aaaar-qarca-cai.
Refer to the Reproducible Build section for information on how to verify the hash of the deployed WebAssembly module.
- Install
dfx. - Cycles wallet with some cycles to pay for requests.
- Commands are executed in
canister/prod.
Example with getSlot
To get the last finalized slot on Solana Mainnet
dfx canister call --ic sol_rpc --wallet $(dfx identity get-wallet --ic) --with-cycles 2B getSlot \
'
(
variant { Default = variant { Mainnet } },
opt record {
responseConsensus = opt variant { Equality };
},
opt record { commitment = opt variant { finalized } },
)'More examples are available here.
- Add the
sol_rpc_clientandsol_rpc_typeslibraries as dependencies in yourCargo.toml. - Follow the steps outlined here to ensure your code compiles.
- If you are running the example locally, follow the instructions here to deploy a local instance of the SOL RPC canister.
Example with getSlot
To get the last finalized slot on Solana Mainnet:
use sol_rpc_client::SolRpcClient;
use sol_rpc_types::{
CommitmentLevel, ConsensusStrategy, GetSlotParams, RpcConfig, RpcSources, SolanaCluster,
};
let client = SolRpcClient::builder_for_ic()
.with_rpc_sources(RpcSources::Default(SolanaCluster::Mainnet))
.with_rpc_config(RpcConfig {
response_consensus: Some(ConsensusStrategy::Equality),
..Default::default()
})
.build();
let slot = client
.get_slot()
.with_params(GetSlotParams {
commitment: Some(CommitmentLevel::Finalized),
..Default::default()
})
.send()
.await;Full examples are available in the examples folder and additional code snippets are also available in the sol_rpc_client crate.
Tip
When deploying your own instance of the SOL RPC canister, you will need to provide and manage your own API keys for the Solana RPC providers. You can provision these keys with the updateApiKeys canister endpoint.
To deploy your own instance of the SOL RPC canister to the IC Mainnet, first add the following to your dfx.json:
{
"canisters": {
"sol_rpc": {
"type": "custom",
"candid": "https://github.com/dfinity/sol-rpc-canister/releases/latest/download/sol_rpc_canister.did",
"wasm": "https://github.com/dfinity/sol-rpc-canister/releases/latest/download/sol_rpc_canister.wasm.gz",
"init_arg": "( record {} )"
}
}
}You can also specify your own init_args to configure the SOL RPC canister's behaviour. For this, refer to the Candid interface specification.
Finally, run the following command (from the directory containing your dfx.json) to deploy the canister on the IC:
dfx deploy --ic sol_rpcImportant
Deploying the SOL RPC canister locally hides some important differences compared to deploying on the ICP Mainnet. Always test your solution on the ICP Mainnet before considering it production-ready. The following behaviors are possible in local environments but not supported on Mainnet:
- IPv4 HTTP outcalls: Local development environments allow HTTP requests over IPv4, but the ICP Mainnet only supports IPv6 for HTTP outcalls. For example, Solana Foundation public RPC endpoints, which are support only IPv4, will work locally but not on Mainnet.
- Single-replica behavior: Local deployments run on a single replica, while Mainnet uses a replicated, consensus-based model. This can cause calls that work locally to fail on Mainnet due to consensus issues. For instance, calls to
getLatestBlockhashmay succeed locally but fail on Mainnet because Solanaβs fast block times can cause discrepancies between replicas during validation.
To deploy a local instance of the SOL RPC canister, first add the following to your dfx.json config file:
{
"canisters": {
"sol_rpc": {
"type": "custom",
"candid": "https://github.com/dfinity/sol-rpc-canister/releases/latest/download/sol_rpc_canister.did",
"wasm": "https://github.com/dfinity/sol-rpc-canister/releases/latest/download/sol_rpc_canister.wasm.gz",
"remote": {
"id": {
"ic": "tghme-zyaaa-aaaar-qarca-cai"
}
},
"init_arg": "( record {} )"
}
}
}You can also specify your own init_args to configure the SOL RPC canister's behaviour. For this, refer to the Candid interface specification.
Finally, run the following commands (from the directory containing your dfx.json) to deploy the canister in your local environment:
# Start the local replica
dfx start --background
# Locally deploy the `sol_rpc` canister
dfx deploy sol_rpcTo deploy the SOL RPC canister together with an example Solana wallet smart contract using ICP Ninja, click on the following link:
Tip
If you download the project from ICP Ninja to deploy it locally, you will need to change the init_arg for the basic_solana canister in the dfx.json file. Specifically, you will need to change ed25519_key_name = opt variant { MainnetTestKey1 } to ed25519_key_name = opt variant { LocalDevelopment }. To learn more about the initialization arguments, see the InitArg type in basic_solana.did.
The SOL RPC canister reaches the Solana JSON-RPC providers using HTTPS outcalls and are therefore subject to the following limitations:
- The contacted providers must support IPv6.
- Some Solana RPC endpoint cannot be supported. This is the case for example for
getLatestBlockhash. The reason is that an HTTPs outcalls involves an HTTP request from each node in the subnet and has therefore a latency in the order of a few seconds. This can be problematic for endpoints with fast changing responses, such asgetLatestBlockhash(which changes roughly every 400ms), since in this case nodes will not be able to reach a consensus. - Note that in some cases, the use of a response transformation
to canonicalize the response seen by each node before doing consensus may alleviate the problem.
The exact transform used depends on the Solana method being queried. See the section on Supported methods for more details.
For example,
getSlotrounds by default the received slot by 20 (configurable by the caller), therefore artificially increasing the slot time seen by each node to 8s to allow them reaching consensus with some significantly higher probability. The reason why such a canonicalization strategy does not work forgetLatestBlockhashis that the result is basically a random-looking string of fixed length. - There are therefore two options to send a transaction on Solana using the SOL RPC canister (see the examples)
- Use a durable nonce instead of a blockhash.
- Retrieve a recent blockhash by first retrieving a recent slot with
getSlotand then getting the block (which includes the blockhash) withgetBlock.
The limitations described above imply that it is sometimes necessary to adapt a raw response from a Solana endpoint to increase the likelihood of nodes reaching consensus when querying that endpoint using HTTPS outcalls. The table below summarizes the supported endpoints and the necessary changes (if any) made to the response indicated as follows:
- β no changes are made to the raw response (excepted for JSON canonicalization).
- βοΈ one or several fields are either not supported in the request parameters, or removed from the raw response.
- π οΈ the raw response is more heavily transformed (e.g. rounding, subset, etc.).
| Solana method | Support | Known limitations |
|---|---|---|
getAccountInfo |
βοΈ |
|
getBalance |
βοΈ |
|
getBlock |
βοΈ |
|
getRecenPrioritizationFees |
π οΈ |
|
getSignaturesForAddress |
β |
|
getSignatureStatuses |
βοΈ |
|
getSlot |
π οΈ |
|
getTokenAccountBalance |
βοΈ |
|
getTransaction |
βοΈ |
|
sendTransaction |
β |
| Provider | Solana Mainnet | Solana Devnet |
|---|---|---|
| Alchemy | β | β |
| Ankr | β | β |
| Chainstack | β | β |
| dRPC | β | β |
| Helius | β | β |
| PublicNode | β | β |
The SOL RPC canister supports reproducible builds:
- Ensure Docker is installed on your machine.
- Run
docker-buildin your terminal. - Run
sha256sum sol_rpc_canister.wasm.gzon the generated file to view the SHA-256 hash.
In order to verify the latest SOL RPC Wasm file, please make sure to download the corresponding version of the source code from the latest GitHub release.
- π₯ Demo that runs through most parts of the basic_solana example.
- π° Blog post ICP Reaches the Shores of Solana.
- π’ Forum post on the developer forum.
At this point we do not accept external contributions yet. External contributions will be accepted after the initial release.
- Run the
Releaseworkflow by clicking onRun workflow. The branch to use to run the workflow is typicallymain. - This will open a
Draft PRwith the labelrelease.- Adapt the changelogs as needed.
- Go through the usual review process and merge when satisfied.
- Run the
Publishworkflow by clicking onRun workflow. The branch to use to run the workflow is typicallymain. The job will do the following:- Create Git tags.
- Publish crates on crates.io.
- Create a Github pre-release.
This project is licensed under the Apache License 2.0.