> ## Documentation Index
> Fetch the complete documentation index at: https://sava.software/llms.txt
> Use this file to discover all available pages before exploring further.

# Ravina Solana

> Components to ease the development of Solana based services.

<Card title="GitHub Repository" icon="github" horizontal={true} href="https://github.com/sava-software/ravina" />

## [Dependencies](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/module-info.java)

* java.net.http
* [org.bouncycastle.provider](https://www.bouncycastle.org/download/bouncy-castle-java/#latest)
* software.sava.json.iterator
* software.sava.core
* software.sava.rpc
* software.sava.solana\_programs
* software.sava.anchor\_src\_gen
* software.sava.anchor\_programs
* software.sava.ravina\_core
* software.sava.kms\_core

## RPC Call Weights

### RPC Call Weights Construction

Used to match the request capacity available for a given RPC provider.

```java theme={null}
var jsonConfig = """
    {
      "getProgramAccounts": 2,
      "getTransaction": 5,
      "sendTransaction": 10
    }
    """;

var ji = JsonIterator.parse(jsonConfig);

var rpcCallWeights = CallWeights.parse(ji);
```

## RPC Caller

Ties together a [Load Balancer](/libraries/ravina/core#load-balancer), [RPC Call Weights](/libraries/ravina/solana#rpc-call-weights) and an Executor Service to make using remote calls more convenient.

### RPC Caller Construction

```java theme={null}
LoadBalancer<SolanaRpcClient> loadBalancer; // See LoadBalancer in Ravina Core
var rpcCallWeights = CallWeights.createDefault();
var executorService = Executors.newVirtualThreadPerTaskExecutor();

var rpcCaller = new RpcCaller(executorService, loadBalancer, rpcCallWeights);

var getEpochInfoCallContext = CallContext.createContext(
    1,    // Call weight.
    0,    // Minimum capacity required before sending.
    1,    // Maximum times to try to claim capacity.
    true, // Eventually force the call.
    0,    // Maximum times to retry call.
    true  // Measure the call time to be tracked by the load balancer for prioritizing items.
);

var epochInfo = rpcCaller.courteousGet(
    SolanaRpcClient::getEpochInfo,
    getEpochInfoCallContext,
    "rpcClient::getEpochInfo"
);
```

## [Epoch Info Service](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/epoch/EpochInfoService.java)

Periodically polls for slot performance stats and epoch information to create
an [Epoch data record](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/epoch/Epoch.java)
which provides the following features:

* The duration remaining for a given time unit.
* Percent complete.
* Epochs per year.
* The current slot index.
* The current block height accounting for a skip rate.
* [SlotPerformanceStats](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/epoch/SlotPerformanceStats.java)

### [Epoch Service Configuration](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/epoch/EpochServiceConfig.java)

* **defaultMillisPerSlot**: Used if performance samples are not available.
* **minMillisPerSlot**: Performance samples will be capped to greater than or equal to this value.
* **maxMillisPerSlot**: Performance samples will be capped to less than or equal to this value.
* **slotSampleWindow**: The recent time window to draw performance samples.
* **fetchSlotSamplesDelay**: Poll new performance samples and epoch information delay.
* **fetchEpochInfoAfterEndDelay**: Also fetch new performance samples and epoch information after each epoch rollover.

```json theme={null}
{
  "defaultMillisPerSlot": 410,
  "minMillisPerSlot": 390,
  "maxMillisPerSlot": 500,
  "slotSampleWindow": "13M",
  "fetchSlotSamplesDelay": "8M",
  "fetchEpochInfoAfterEndDelay": "0.5S"
}
```

### Epoch Info Service Usage

```java theme={null}
RpcCaller rpcCaller; // See the documentation above.

var jsonConfig = "null";
var epochServiceConfig = EpochServiceConfig.parseConfig(ji);
if (epochServiceConfig == null) {
  epochServiceConfig = EpochServiceConfig.createDefault();
}
var epochInfoService = EpochInfoService.createService(epochServiceConfig, rpcCaller);

var executorService = Executors.newSingleThreadExecutor();
executorService.execute(epochInfoService);

epochInfoService.awaitInitialized();

var epochInfo = epochInfoService.epochInfo();
System.out.println(epochInfo);
```

## [Web Socket Manager](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/websocket/WebSocketManager.java)

Used to manage the connection of a SolanaRpcWebsocket. Call `checkConnection` to drive re-connections. If errors are
observed, the corresponding delay from the provided [Backoff](/libraries/ravina/core#backoff) will be respected.

### Pseudo Web Socket Manager Usage

```java theme={null}
var backoff = Backoff.exponential(1, 32);
var account = PublicKey.fromBase58Encoded("");

var httpClient = HttpClient.newHttpClient();
var webSocketManager = WebSocketManager.createManager(
    httpClient,
    URI.create("wss://url"),
    backoff,
    webSocket -> webSocket.accountSubscribe(account, System.out::println)
);

for (;;) {
    webSocketManager.checkConnection();
    Thread.sleep(1_000);
}
```

### [Web Socket Manager Configuration](https://github.com/sava-software/ravina/blob/main/ravina-core/src/main/java/software/sava/services/core/config/RemoteResourceConfig.java)

* **endpoint**: websocket url.
* **backoff**: Used for connection attempts in response to failures.

## [Chain Item Formatter](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/config/ChainItemFormatter.java)

Used to provide more convenient logging of accounts and transaction hashes.

### Formatter Usage

```java theme={null}
var jsonConfig = """
    {
      "sig": "https://solscan.io/tx/%s",
      "address": "https://solscan.io/account/%s"
    }
    """;
var ji = JsonIterator.parse(jsonConfig);
var formatter = ChainItemFormatter.parseFormatter(ji);
System.out.println(formatter.formatSig("5yem9DTy4mHa94yKVCSuQYixvtt27aF2L2bcms8jsgv2Yvgj2FY4DXYpUmN5TBvRX8xC99R6ea1k5eJJb2hVGmxF"));
```

## [Lookup Table Cache](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/alt/LookupTableCache.java)

May be used to dynamically fetch lookup tables. Ideally used in conjunction with a websocket to maintain the latest view of a table.
The current implementation only removes tables if they are no longer active or no longer exist onchain.

### Lookup Table Cache Usage

```java theme={null}
var taskExecutor = Executors.newVirtualThreadPerTaskExecutor();
var tableCache = LookupTableCache.createCache(
    taskExecutor,
    128, // initial map capacity
    rpcCaller.rpcClients(),
    AddressLookupTable.FACTORY
);

AddressLookupTable table = tableCache.getOrFetchTable(PublicKey.fromBase58Encoded(""));
```

## [Transaction Processor](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/transactions/TransactionProcessor.java)

Convenience methods for creating, simulating, signing, and sending transactions.

### Transaction Processor Construction

```java theme={null}
SigningService signingService; // See the Sava KMS project.
var servicePublicKey = signingService.publicKey().join();

WebSocketManager webSocketManager; // See the documentation above.

LoadBalancer<SolanaRpcClient> rpcClients; // See LoadBalancer in Ravina Core
LoadBalancer<SolanaRpcClient> sendClients; // Used only for sending transactions.
LoadBalancer<FeeProvider> feeProviders; // Provides priority fees given a transaction.
var callWeights = CallWeights.createDefault();

var formatter = ChainItemFormatter.createDefault();
var solanaAccounts = SolanaAccounts.MAIN_NET;

var taskExecutor = Executors.newVirtualThreadPerTaskExecutor();

var tableCache = LookupTableCache.createCache(taskExecutor, 128, rpcCaller.rpcClients());

var transactionProcessor = TransactionProcessor.createProcessor(
    taskExecutor,
    signingService,
    tableCache,
    servicePublicKey,
    solanaAccounts,
    formatter,
    rpcClients,
    sendClients,
    feeProviders,
    callWeights,
    webSocketManager
);
```

## [Transaction Monitor Service](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/transactions/TxMonitorService.java)

Provides functionality for tracking transaction confirmation via asynchronous and polling signature status RPC calls.
Up to 256 transaction signatures are checked for each polling call.
It is recommended to use this with the Instruction or Batch Instruction Services, rather than directly.

### Transaction Monitor Service Construction

```java theme={null}
// See the documentation above for construction of these components.
WebSocketManager webSocketManager; 
RpcCaller rpcCaller;
EpochInfoService epochInfoService;

// TransactionProcessor extends this interface, otherwise a simpler implementation may be provided.
TxPublisher txPublisher;

var formatter = ChainItemFormatter.createDefault();

var txMonitorJsonConfig = """
    {
      "webSocketConfirmationTimeout": "5S",
      "minSleepBetweenSigStatusPolling": "3S",
      "retrySendDelay": "5S",
      "minBlocksRemainingToResend": 8
    }""";
var ji = JsonIterator.parse(txMonitorJsonConfig);

var txMonitorConfig = TxMonitorConfig.parseConfig(ji);

var txMonitorService = TxMonitorService.createService(
    formatter,
    rpcCaller,
    epochInfoService,
    webSocketManager,
    txMonitorConfig,
    txPublisher
);
```

## [Instruction Service](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/transactions/InstructionService.java)

Handles transaction simulation, creation, and confirmation/expiration monitoring.

The transaction is simulated against a Confirmed state.

The blockhash and its height is taken from the simulation result to enable expiration tracking.

After sending a transaction, a confirmed state signature status Web Socket subscription is made, which expires based on the Transaction Monitor configuration.

If a response message is received over the Web Socket and a finalized state is desired an additional subscription is made, which expires two times the estimated duration of 32 blocks.

If the Web Socket subscriptions time out the service falls back to polling signature status calls.
The monitor will make at least one call without transaction history and optionally continue to do so until the transactions' block has expired.
After which one final call is made with transaction history enabled to ensure the transaction did not land but was expired from the recent history cache.

While waiting for expiration the transaction may optionally be re-sent based on the Transaction Monitor configuration.

### Instruction Service Usage

```java theme={null}
// See the documentation above for construction of these components.
EpochInfoService epochInfoService;
RpcCaller rpcCaller;
TransactionProcessor transactionProcessor;
TxMonitorService txMonitorService;

var nativeProgramClient = NativeProgramClient.createClient();

var instructionService = InstructionService.createService(
    rpcCaller,
    transactionProcessor,
    nativeProgramClient,
    epochInfoService,
    txMonitorService
);

// Populated from the Jupiter API.
List<Instruction> jupiterSwapInstructions;
List<PublicKey> lookupTableKeys;

var maxSOLPriorityFee = new BigDecimal("0.001");
var maxLamportPriorityFee = LamportDecimal.fromBigDecimal(maxSOLPriorityFee);

var transactionResult = instructionService.processInstructions(
    1.1, // Compute budget multiplier applied against simulation consumption.
    jupiterSwapInstructions,
    maxLamportPriorityFee, // Caps the priority fee from the Transaction Processor's Fee Provider.
    FINALIZED, // Await finalization if successful.
    CONFIRMED, // Only await confirmed if fails, e.g., slippage exceeded.
    true, // Verify expired, if the network is not aware of the transaction wait 151 blocks to ensure it cannot be applied.
    true, // Continue to re-send the transaction based on the Transaction Monitor configuration.
    0, // Number of times to re-construct the transaction with a new block hash after expiration.
    transactionProcessor.transactionFactory(lookupTableKeys), // Uses an internal address lookup table cache.
    "jupiter swap" // log context.
);
```

## [Batch Instruction Service](https://github.com/sava-software/ravina/blob/main/ravina-solana/src/main/java/software/sava/services/solana/transactions/BaseBatchInstructionService.java)

Extends the Instruction Service functionality by dynamically batching multiple instructions which may exceed the size of a single transaction into multiple transactions.
