Skip to main content

Quickstart

This guide will help you get started with Sava quickly. We’ll create a keypair, connect to Solana, and fetch some on-chain data.
Make sure you’ve completed the Installation steps before continuing.

Your First Sava Application

Let’s build a simple application that demonstrates core Sava functionality.
1

Create a Keypair

First, let’s generate an Ed25519 keypair for signing transactions:
import software.sava.core.accounts.Signer;
import software.sava.core.accounts.PublicKey;

// Generate a new random keypair
byte[] keyPair = Signer.generatePrivateKeyPairBytes();
Signer signer = Signer.createFromKeyPair(keyPair);

// Get the public key
PublicKey publicKey = signer.publicKey();
System.out.println("Public Key: " + publicKey.toBase58());
The keypair bytes contain both the private key (first 32 bytes) and public key (last 32 bytes).
You can also create a signer from an existing private key:
byte[] privateKey = // ... your 32-byte private key
Signer signer = Signer.createFromPrivateKey(privateKey);
2

Connect to Solana

Create an RPC client to connect to a Solana cluster:
import software.sava.rpc.json.http.client.SolanaRpcClient;
import software.sava.rpc.json.http.SolanaNetwork;
import java.net.http.HttpClient;

// Create HTTP client
HttpClient httpClient = HttpClient.newHttpClient();

// Connect to Solana mainnet
SolanaRpcClient rpcClient = SolanaRpcClient.createClient(
    SolanaNetwork.MAIN_NET.getEndpoint(),
    httpClient
);
Sava provides pre-configured endpoints for all networks:
  • SolanaNetwork.MAIN_NET - Mainnet Beta
  • SolanaNetwork.DEV_NET - Devnet
  • SolanaNetwork.TEST_NET - Testnet
3

Fetch Account Information

Query account data from the blockchain:
import software.sava.rpc.json.http.response.AccountInfo;

// Fetch account info
PublicKey accountToQuery = PublicKey.fromBase58Encoded(
    "7ubS3GccjhQY99AYNKXjNJqnXjaokEdfdV915xnCb96r"
);

AccountInfo<byte[]> accountInfo = rpcClient
    .getAccountInfo(accountToQuery)
    .join();

System.out.println("Account Owner: " + accountInfo.owner().toBase58());
System.out.println("Lamports: " + accountInfo.lamports());
System.out.println("Data Length: " + accountInfo.data().length);
All RPC methods return CompletableFuture for async operations. Use .join() to wait for the result.
4

Check Account Balance

Get the SOL balance of any account:
import software.sava.rpc.json.http.response.Lamports;

Lamports balance = rpcClient
    .getBalance(publicKey)
    .join();

System.out.println("Balance: " + balance.lamports() + " lamports");
System.out.println("Balance: " + (balance.lamports() / 1_000_000_000.0) + " SOL");
1 SOL = 1,000,000,000 lamports

Complete Example

Here’s a complete working example that ties everything together:
import software.sava.core.accounts.PublicKey;
import software.sava.core.accounts.Signer;
import software.sava.rpc.json.http.SolanaNetwork;
import software.sava.rpc.json.http.client.SolanaRpcClient;
import software.sava.rpc.json.http.response.AccountInfo;
import software.sava.rpc.json.http.response.Lamports;

import java.net.http.HttpClient;

public class SavaQuickstart {
    
    public static void main(String[] args) {
        // Generate a keypair
        byte[] keyPair = Signer.generatePrivateKeyPairBytes();
        Signer signer = Signer.createFromKeyPair(keyPair);
        PublicKey publicKey = signer.publicKey();
        
        System.out.println("Generated Public Key: " + publicKey.toBase58());
        
        // Connect to Solana
        try (HttpClient httpClient = HttpClient.newHttpClient()) {
            SolanaRpcClient rpcClient = SolanaRpcClient.createClient(
                SolanaNetwork.MAIN_NET.getEndpoint(),
                httpClient
            );
            
            // Query a well-known account
            PublicKey accountToQuery = PublicKey.fromBase58Encoded(
                "7ubS3GccjhQY99AYNKXjNJqnXjaokEdfdV915xnCb96r"
            );
            
            // Fetch account info
            AccountInfo<byte[]> accountInfo = rpcClient
                .getAccountInfo(accountToQuery)
                .join();
            
            System.out.println("\nAccount Information:");
            System.out.println("  Owner: " + accountInfo.owner().toBase58());
            System.out.println("  Lamports: " + accountInfo.lamports());
            System.out.println("  Executable: " + accountInfo.executable());
            System.out.println("  Data Length: " + accountInfo.data().length);
            
            // Get balance
            Lamports balance = rpcClient
                .getBalance(accountToQuery)
                .join();
            
            double solBalance = balance.lamports() / 1_000_000_000.0;
            System.out.println("\nBalance: " + solBalance + " SOL");
        }
    }
}

Working with Transactions

Here’s a more advanced example showing how to parse transaction data:
import software.sava.core.tx.TransactionSkeleton;
import software.sava.rpc.json.http.client.SolanaRpcClient;
import software.sava.rpc.json.http.SolanaNetwork;
import java.net.http.HttpClient;

public class TransactionExample {
    
    public static void main(String[] args) {
        try (HttpClient httpClient = HttpClient.newHttpClient()) {
            SolanaRpcClient rpcClient = SolanaRpcClient.createClient(
                SolanaNetwork.MAIN_NET.getEndpoint(),
                httpClient
            );
            
            // Fetch a transaction by signature
            String signature = "3uoR4BXmEo1PiXUrVAEC8kr6Hu619WsTU7obkUGxNAnpAhLgkLNC6WsWuKn7S9PTXWCt6er28ed6q9SpJbPiFLgC";
            var tx = rpcClient.getTransaction(signature).join();
            
            // Parse the transaction structure
            var skeleton = TransactionSkeleton.deserializeSkeleton(tx.data());
            var instructions = skeleton.parseLegacyInstructions();
            
            System.out.println("Transaction has " + instructions.length + " instructions");
            
            // Inspect transaction logs
            tx.meta().logMessages().forEach(log -> 
                System.out.println("  Log: " + log)
            );
        }
    }
}

Common Operations

Get Latest Block Hash

import software.sava.rpc.json.http.response.LatestBlockHash;

LatestBlockHash blockHash = rpcClient.getLatestBlockHash().join();
System.out.println("Block Hash: " + blockHash.blockHash().toBase58());
System.out.println("Last Valid Block Height: " + blockHash.lastValidBlockHeight());

Get Slot Information

Long currentSlot = rpcClient.getSlot().join();
System.out.println("Current Slot: " + currentSlot);

Get Multiple Accounts

import java.util.List;

List<PublicKey> accounts = List.of(
    PublicKey.fromBase58Encoded("account1..."),
    PublicKey.fromBase58Encoded("account2..."),
    PublicKey.fromBase58Encoded("account3...")
);

List<AccountInfo<byte[]>> accountInfos = rpcClient
    .getAccounts(accounts)
    .join();

accountInfos.forEach(info -> 
    System.out.println(info.pubKey().toBase58() + ": " + info.lamports())
);

Using Commitment Levels

Sava supports all Solana commitment levels for data consistency:
import software.sava.rpc.json.http.request.Commitment;

// Fetch with specific commitment level
AccountInfo<byte[]> confirmedInfo = rpcClient
    .getAccountInfo(Commitment.CONFIRMED, publicKey)
    .join();

AccountInfo<byte[]> finalizedInfo = rpcClient
    .getAccountInfo(Commitment.FINALIZED, publicKey)
    .join();
Available commitment levels:
  • Commitment.PROCESSED - Fastest, least reliable
  • Commitment.CONFIRMED - Default, good balance
  • Commitment.FINALIZED - Slowest, most reliable

Signing Messages

Sign arbitrary messages with your keypair:
String message = "Hello, Solana!";
byte[] signature = signer.sign(message.getBytes());

// Verify the signature
boolean isValid = publicKey.verifySignature(message, signature);
System.out.println("Signature valid: " + isValid);

Error Handling

Handle RPC errors gracefully:
import software.sava.rpc.json.http.response.JsonRpcException;
import java.util.concurrent.CompletionException;

try {
    AccountInfo<byte[]> info = rpcClient
        .getAccountInfo(publicKey)
        .join();
} catch (CompletionException e) {
    if (e.getCause() instanceof JsonRpcException rpcError) {
        System.err.println("RPC Error: " + rpcError.getMessage());
        System.err.println("Error Code: " + rpcError.code());
    } else {
        throw e;
    }
}

Next Steps

Now that you’ve seen the basics, explore more advanced topics:

Core Concepts

Learn about accounts, transactions, and programs

RPC API Reference

Explore all available RPC methods

Working with Tokens

Build token-based applications

Transaction Building

Create and send transactions
All code examples in this guide are tested and work with the latest version of Sava. Check the GitHub repository for more examples.

Build docs developers (and LLMs) love