Skip to content

Deanly/solana-sdkj

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

687 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Solana SDK for Java (solana-sdkj)

License: MIT Java Version Documentation Maven Central

solana-sdkj is a modern, developer-friendly SDK for building Java applications on the Solana blockchain.
It provides complete RPC access, convenient abstractions for System Programs, and first-class support for Borsh-encoded program states using struct-layout.


✨ Features

  • Full support for all Solana RPC methods
  • Built-in abstractions for SystemProgram, Transactions, Program Derived Addresses, and VersionedMessage (v0)
  • Strong Borsh/Rust/C state decoding via @StructLayout
    Easily map program account data into structured Java classes
    Extend the State class and pair it with getAccountState() to decode any base64-encoded account with full type safety
  • Safer and more maintainable than jsonParsed — no runtime casting, no fragile field access.
  • Clean interoperability with custom programs and PDA-based layouts
  • Java 17+ compatible, with secure cryptography powered by BouncyCastle

📦 Dependencies

solana-sdkj depends on the following libraries:


🏗️ Installation

You can include solana-sdkj in your project via Maven. Add the following dependency to your pom.xml:

<dependency>
  <groupId>net.deanly</groupId>
  <artifactId>solana-sdkj</artifactId>
  <version>0.1.2</version>
</dependency>

If you're using Gradle, add the following to your build.gradle:

implementation 'net.deanly:solana-sdkj:0.1.2'

🚀 Getting Started

Retrieve Account Balance

solana-sdkj lets you easily interact with Solana through the RPC API and built-in program abstractions. Here is a quick example:

import net.deanly.solana.sdk.rpc.client.RpcClient;
import net.deanly.solana.sdk.rpc.client.config.ClientConfig;
import net.deanly.solana.sdk.rpc.client.config.Network;
import net.deanly.solana.sdk.types.PublicKey;

public class Example {
  public static void main(String[] args) throws Exception {
    RpcClient rpcClient = new RpcClient(ClientConfig.builder()
            .network(Network.DEVNET)
            .build());

    // Example: Retrieve Balance
    PublicKey publicKey = new PublicKey("YourPublicKeyHere");
    long balance = rpcClient.getRpcHttpApi().getBalance(publicKey);

    System.out.println("Balance: " + balance);
  }
}

Transfer SOL (Lamports)

This example demonstrates how to transfer SOL (measured in Lamports) from one wallet to another.

import net.deanly.solana.sdk.crypto.KeyPair;
import net.deanly.solana.sdk.crypto.PublicKey;
import net.deanly.solana.sdk.program.core.system.SystemProgram;
import net.deanly.solana.sdk.rpc.client.RpcClient;
import net.deanly.solana.sdk.rpc.client.config.ClientConfig;
import net.deanly.solana.sdk.rpc.client.config.Network;
import net.deanly.solana.sdk.transaction.Transaction;

public class TransferExample {
  public static void main(String[] args) {
    // Configure Solana RPC
    RpcClient rpcClient = new RpcClient(ClientConfig.builder().network(Network.DEVNET).build());

    try {
      // Account setup
      PublicKey senderPublicKey = new PublicKey("YOUR_SENDER_PUBLIC_KEY");
      PublicKey receiverPublicKey = new PublicKey("YOUR_RECEIVER_PUBLIC_KEY");
      KeyPair senderKeyPair = new KeyPair("YOUR_SENDER_PRIVATE_KEY_IN_BASE58".getBytes());

      // Amount to transfer (in lamports, 1 SOL = 10^9 lamports)
      long lamports = 1000000L;

      // Build transaction
      Transaction transaction = new Transaction();
      transaction.addInstruction(
              SystemProgram.transfer(senderPublicKey, receiverPublicKey, lamports)
      );
      transaction.setSigner(senderKeyPair);

      // Send transaction
      String transactionSignature = rpcClient.getRpcHttpApi().sendTransaction(transaction).getValue();
      System.out.println("Transaction sent successfully! Signature: " + transactionSignature);
    } catch (Exception e) {
      System.err.println("Failed to complete transfer: " + e.getMessage());
    }
  }
}

Simulate Transaction

Use the simulateTransaction RPC method to test the validity of a Solana transaction without executing it on-chain.

import net.deanly.solana.sdk.rpc.client.RpcClient;
import net.deanly.solana.sdk.rpc.client.config.ClientConfig;
import net.deanly.solana.sdk.rpc.client.config.Network;
import net.deanly.solana.sdk.crypto.KeyPair;
import net.deanly.solana.sdk.crypto.PublicKey;
import net.deanly.solana.sdk.transaction.Transaction;
import net.deanly.solana.sdk.transaction.instruction.TransactionInstruction;
import net.deanly.solana.sdk.program.core.system.SystemProgram;
import net.deanly.solana.sdk.types.Blockhash;
import net.deanly.solana.sdk.rpc.request.config.SimulateTransactionConfig;
import net.deanly.solana.sdk.rpc.response.ResValueSimulatedTransaction;
import net.deanly.structlayout.StructLayout;

public class SimulateTransactionExample {
  public static void main(String[] args) {
    // Configure Solana RPC
    RpcClient rpcClient = new RpcClient(ClientConfig.builder().network(Network.DEVNET).build());

    try {
      // Fetch recent blockhash
      Blockhash recentBlockhash = rpcClient.getRpcHttpApi()
              .getLatestBlockhash()
              .getValue()
              .getBlockhash();

      // Account setup
      PublicKey senderPublicKey = new PublicKey("YOUR_SENDER_PUBLIC_KEY");
      PublicKey receiverPublicKey = new PublicKey("YOUR_RECEIVER_PUBLIC_KEY");
      KeyPair senderKeyPair = new KeyPair("YOUR_SENDER_PRIVATE_KEY_IN_BASE58".getBytes());

      // Transaction setup
      long lamports = 100000L; // Amount in lamports
      Transaction transaction = new Transaction();
      transaction.setRecentBlockhashForCompile(recentBlockhash);
      transaction.addInstruction(
              SystemProgram.transfer(senderPublicKey, receiverPublicKey, lamports)
      );
      transaction.sign(senderKeyPair);

      // Simulate transaction
      var config = SimulateTransactionConfig.builder().skipPreflight(true).build();
      ResValueSimulatedTransaction simulateResponse = rpcClient.getRpcHttpApi()
              .simulateTransaction(transaction, config).getValue();

      // Print results
      StructLayout.debug(transaction);
      System.out.println("Simulation Result: " + simulateResponse);
    } catch (Exception e) {
      System.err.println("Simulation failed: " + e.getMessage());
    }
  }
}

Sample Output

Order             Field                       Offset  Bytes (HEX)
=======================================================================
1[].length        signatures                  0000000 01
1[0]              signatures                  0000001 39 CD A1 D0 56 A4 08 6B 63 D6 F3 BA 0C FA 32 F2 F9 35 BE 7F 87 F4 10 C4 26 45 B1 AE 26 0D A9 21 9C D3 8A 0B 45 E9 F6 C7 B3 65 F6 08 A4 0E 2B 85 A7 71 0B 3E 73 05 58 DE 66 92 E6 69 4A 73 AC 05
2-1-1             numRequiredSignatures       0000065 01
2-1-2             numReadonlySignedAccounts   0000066 00
2-1-3             numReadonlyUnsignedAccounts 0000067 01
2-2[].length      staticAccountKeys           0000068 03
2-2[0]            staticAccountKeys           0000069 B8 A3 A3 BD 09 5C 2D F5 D4 68 BE 74 E4 A8 29 C4 F2 52 44 77 79 3D 52 19 29 EE 73 C3 43 84 41 F2
2-2[1]            staticAccountKeys           0000101 22 6B FA 70 D4 05 93 83 03 CA 98 BD B5 A2 BA FA 8C 47 4D B5 38 FE 1B F2 5A 65 49 7F A8 3F DF F7
2-2[2]            staticAccountKeys           0000133 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2-3               recentBlockhash             0000165 50 29 81 53 E9 3F 5F D5 A7 38 A4 FF 3F AA AF 4E 4A 27 70 BA 70 6F 41 97 47 C1 F0 11 A3 88 D5 27
2-4[].length      instructions                0000197 01
2-4[0]-1          programIdIndex              0000198 02
2-4[0]-2[].length accountKeyIndexes           0000199 02
2-4[0]-2[0]       accountKeyIndexes           0000200 00
2-4[0]-2[1]       accountKeyIndexes           0000201 01
2-4[0]-3          data                        0000202 0C 03 00 00 00 40 42 0F 00 00 00 00 00
=======================================================================
Total Bytes: 215
Simulation Result: ResValueSimulatedTransaction(err=null, logs=[Program 11111111111111111111111111111111 invoke [1], Program 11111111111111111111111111111111 success], accounts=null, unitsConsumed=150, returnData=null, innerInstruction=[])

The simulateTransaction function allows developers to test transactions before committing them to the blockchain. The result will indicate if the transaction would succeed or fail and, if it fails, what issues to address.

Read Account State via StructLayout (TokenMetadata Example)

You can decode a Solana account’s data into a structured class (State) using getAccountState. This makes it easy to work with Borsh-encoded on-chain data, such as Metaplex’s Token Metadata.

import net.deanly.solana.sdk.rpc.client.RpcClient;
import net.deanly.solana.sdk.rpc.client.config.ClientConfig;
import net.deanly.solana.sdk.rpc.client.config.Network;
import net.deanly.solana.sdk.types.PublicKey;
import net.deanly.solana.sdk.types.ProgramDerivedAddress;
import net.deanly.solana.sdk.rpc.response.ResValueAccountInfo;
import net.deanly.solana.sdk.rpc.request.config.AccountInfoConfig;
import net.deanly.solana.sdk.types.Encoding;
import net.deanly.solana.sdk.program.metaplex.tokenmetadata.state.TokenMetadataState;

import java.nio.charset.StandardCharsets;
import java.util.List;

public class ReadTokenMetadataExample {
  public static void main(String[] args) throws Exception {
    RpcClient rpcClient = new RpcClient(ClientConfig.builder()
            .network(Network.MAINNET)
            .build());

    // Target Mint Address
    String mintBase58 = "6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN";
    PublicKey mint = PublicKey.valueOf(mintBase58);

    // Metaplex Token Metadata Program ID
    PublicKey metadataProgramId = TokenMetadataProgram.PROGRAM_ID;

    // Derive Metadata PDA
    List<byte[]> seeds = List.of(
            "metadata".getBytes(StandardCharsets.UTF_8),
            metadataProgramId.toByteArray(),
            mint.toByteArray()
    );
    ProgramDerivedAddress pda = PublicKey.findProgramAddress(seeds, metadataProgramId);
    System.out.println("Metadata PDA: " + pda.getAddress().toBase58());

    // Fetch and decode state directly
    TokenMetadataState state = rpcClient.getRpcHttpApi().getAccountState(pda.getAddress(), TokenMetadataState.class);

    System.out.println("Decoded Token Metadata:");
    System.out.println(state);
  }
}

Sample Output

TokenMetadataState(
  key=4,
  updateAuthority=5e2qRc1DNEXmyxP8qwPwJhRWjef7usLyi7v5xjqLr5G7,
  mint=6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN,
  name=OFFICIAL TRUMP,
  symbol=TRUMP,
  uri=https://arweave.net/cSCP0h2n1crjeSWE9KF-XtLciJalDNFs7Vf-Sm0NNY0,
  sellerFeeBasisPoints=0,
  creators=[...],
  primarySaleHappened=false,
  isMutable=false,
  ...
)

getAccountState automatically decodes Borsh-encoded data using the class you provide (which must extend State). You can define your own @StructLayout-annotated types to read custom program accounts.

Read Token Accounts via JSON_PARSED

You can also access raw JSON-parsed account data without defining a typed model, using key-path access:

public static void sample() {
  var address = net.deanly.solana.sdk.crypto.PublicKey.valueOf("");

  RpcResultObject<List<ResValueTokenAccount>> result = rpc.getRpcHttpApi()
          .getTokenAccountsByOwner(
                  address,
                  TokenAccountByOwnerFilter.builder()
                          .programId(SplTokenProgram.PROGRAM_ID)
                          .build(),
                  TokenAccountsByOwnerConfig.builder()
                          .encoding(Encoding.JSON_PARSED)
                          .build());

  for (ResValueTokenAccount value : result.getValue()) {
    String mint = (String) value.getAccount().getData().getObjectValue("parsed.info.mint");
    String amount = (String) value.getAccount().getData().getObjectValue("parsed.info.tokenAmount.uiAmountString");
    System.out.println("Mint: " + mint + ", Amount: " + amount);
  }
}

🤝 Acknowledgement

This project is inspired by solanaj by Michael Morrell. We appreciate the groundwork laid by its contributors and aim to continue that spirit with an extended and modernized architecture tailored for today’s Java developers.


📄 License

Licensed under thee MIT License.

About

Solana RPC Methods for java

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages