GitHub Repository

Dependencies

Program Clients

Provides convenience methods for creating common instructions.

Typical Transaction Flow

Signer signer = ...; // load private key
var feePayer = AccountMeta.createFeePayer(signer.publicKey());

var programClient = NativeProgramClient.createClient();
var programAccountClient = programClient.createAccountClient(feePayer);

try (var httpClient = HttpClient.newHttpClient()) {
  var rpcClient = SolanaRpcClient.createClient(
      SolanaNetwork.MAIN_NET.getEndpoint(),
      httpClient
  );

  // Create relevant instructions
  List<Instruction> instructions = List.of();

  var simulationTransaction = programAccountClient.createTransaction(
      ComputeBudgetProgram.MAX_COMPUTE_BUDGET,
      0, // compute unite price
      instructions
  );

  // Apply a user provided fee or fetch an estimate from a service such as Helius' Fee API.
  long microLamportComputeUnitPrice = 1234;

  var simulationResult = rpcClient.simulateTransaction(simulationTransaction).join();

  var transaction = programAccountClient.createTransaction(
      simulationResult.unitsConsumed().getAsInt(),
      microLamportComputeUnitPrice,
      instructions
  );
  transaction.setRecentBlockHash(simulationResult.replacementBlockHash().blockhash());
  transaction.sign(signer);

  var base64Encoded = transaction.base64EncodeToString();
  var sig = rpcClient.sendTransaction(base64Encoded).join();
  System.out.println(sig);
}

Transfer SOL

var transferFrom = PublicKey.fromBase58Encoded("");
var feePayer = AccountMeta.createFeePayer(transferFrom);

var programClient = NativeProgramClient.createClient();
var programAccountClient = programClient.createAccountClient(feePayer);

var transferTo = PublicKey.fromBase58Encoded("");
var transferIx = programAccountClient.transferSolLamports(transferTo, 42);

Create, Initialize & Delegate Stake Account

long stakeLamports = LamportDecimal.fromBigDecimal(BigDecimal.ONE).longValue();
var stakeAuthority = PublicKey.fromBase58Encoded("");
var feePayer = AccountMeta.createFeePayer(stakeAuthority);
var validatorVoteAccount = PublicKey.fromBase58Encoded("");

var programClient = NativeProgramClient.createClient();
var programAccountClient = programClient.createAccountClient(feePayer);

try (var httpClient = HttpClient.newHttpClient()) {
  
  var rpcClient = SolanaRpcClient.createClient(
      SolanaNetwork.MAIN_NET.getEndpoint(),
      httpClient
  );
  var minRent = NativeProgramClient.getMinimumBalanceForStakeAccount(rpcClient).join();

  var seed = ZonedDateTime.now(ZoneOffset.UTC).toString();
  var uninitializedStakeAccount = programAccountClient.createOffCurveStakeAccountWithSeed(seed);

  var createStakeAccountIx = programAccountClient.createStakeAccountWithSeed(
      uninitializedStakeAccount,
      minRent
  );

  var stakeAccountPubKey = uninitializedStakeAccount.publicKey();
  var initializeIx = programAccountClient.initializeStakeAccount(stakeAccountPubKey);
  var transferIx = programAccountClient.transferSolLamports(stakeAccountPubKey, stakeLamports);
  var delegateStakeIx = programAccountClient.delegateStakeAccount(stakeAccountPubKey, validatorVoteAccount);

  var instructions = List.of(createStakeAccountIx, initializeIx, transferIx, delegateStakeIx);
}

Create & Extend Lookup Table

var programClient = NativeProgramClient.createClient();
var programAccountClient = programClient.createAccountClient(feePayer);

var newAccounts = List.of(
    PublicKey.fromBase58Encoded(""),
    PublicKey.fromBase58Encoded(""),
    PublicKey.fromBase58Encoded("")
);

long recentSlot = rpcClient.getSlot(FINALIZED).join();

var lookupTablePDA = nativeProgramAccountClient.findLookupTableAddress(recentSlot);

var createLookupTableIx = nativeProgramAccountClient.createLookupTable(lookupTablePDA, recentSlot);
var extendTableIx = nativeProgramAccountClient.extendLookupTable(lookupTablePDA.publicKey(), newAccounts);

var instructions = List.of(createLookupTableIx, extendTableIx);

Durable Nonce Transactions

See the official Solana documentation for context on durable nonce transactions.

Create & Initialize Nonce Account

Signer signer = ...
 
var rpcEndpoint = SolanaNetwork.MAIN_NET.getEndpoint();
try (var httpClient = HttpClient.newHttpClient()) {
  var rpcClient = SolanaRpcClient.createClient(rpcEndpoint, httpClient);

  var blockHashFuture = rpcClient.getLatestBlockHash();
  var minRentFuture = rpcClient.getMinimumBalanceForRentExemption(NonceAccount.BYTES);

  var solanaAccounts = SolanaAccounts.MAIN_NET;
  var nonceAccountWithSeed = PublicKey.createOffCurveAccountWithAsciiSeed(
      signer.publicKey(),
      "nonce",
      solanaAccounts.systemProgram()
  );

  var initializeNonceAccountIx = SystemProgram.initializeNonceAccount(
      solanaAccounts,
      nonceAccountWithSeed.publicKey(),
      signer.publicKey()
  );
  
  System.out.format("""
          Fetching block hash and minimum rent to create nonce account %s with authority %s.
          
          """,
      nonceAccountWithSeed.publicKey(),
      signer.publicKey()
  );

  long minRent = minRentFuture.join();
  var createNonceAccountIx = SystemProgram.createAccountWithSeed(
      solanaAccounts.invokedSystemProgram(),
      signer.publicKey(),
      nonceAccountWithSeed,
      minRent,
      NonceAccount.BYTES,
      solanaAccounts.systemProgram()
  );

  var instructions = List.of(createNonceAccountIx, initializeNonceAccountIx);
  var transaction = Transaction.createTx(signer.publicKey(), instructions);

  var blockHash = blockHashFuture.join().blockHash();
  transaction.setRecentBlockHash(blockHash);
  transaction.sign(signer);

  var base64Encoded = transaction.base64EncodeToString();
  var sendTransactionFuture = rpcClient.sendTransaction(base64Encoded);
  System.out.format("""
          Creating nonce account %s
          https://explorer.solana.com/tx/%s
          
          """,
      nonceAccountWithSeed.publicKey(),
      transaction.getBase58Id()
  );

  var sig = sendTransactionFuture.join();
  System.out.format("""
          Confirmed transaction %s
          https://solscan.io/account/%s
          
          """,
      sig,
      nonceAccountWithSeed.publicKey()
  );

  var nonceAccountInfo = rpcClient.getAccountInfo(nonceAccountWithSeed.publicKey()).join();
  var nonceAccount = NonceAccount.read(nonceAccountInfo);
  System.out.println(nonceAccount);
}

Create & Send Durable Nonce Transaction

var signer = ...
var nonceAccountKey = PublicKey.fromBase58Encoded("");
var sendToKey = PublicKey.fromBase58Encoded("");
var transferSOL = new BigDecimal("0.0");

var solanaAccounts = SolanaAccounts.MAIN_NET;
var rpcEndpoint = SolanaNetwork.MAIN_NET.getEndpoint();
try (var httpClient = HttpClient.newHttpClient()) {
  var rpcClient = SolanaRpcClient.createClient(rpcEndpoint, httpClient);

  var nonceAccountInfo = rpcClient.getAccountInfo(nonceAccountKey).join();
  var nonceAccount = NonceAccount.read(nonceAccountInfo);
  System.out.println(nonceAccount);

  var advanceNonceIx = nonceAccount.advanceNonceAccount(solanaAccounts);
  var transferIx = SystemProgram.transfer(
      solanaAccounts.invokedSystemProgram(),
      signer.publicKey(),
      sendToKey,
      LamportDecimal.fromBigDecimal(transferSOL).longValue()
  );

  var instructions = List.of(advanceNonceIx, transferIx);
  var transaction = Transaction.createTx(signer.publicKey(), instructions);
  transaction.setRecentBlockHash(nonceAccount.nonce());
  transaction.sign(signer);

  var base64Encoded = transaction.base64EncodeToString();
  var sendTransactionFuture = rpcClient.sendTransaction(base64Encoded);
  System.out.format("""
          Transferring %s SOL from %s to %s.
          https://explorer.solana.com/tx/%s
          
          """,
      transferSOL.toPlainString(), signer.publicKey(), sendToKey,
      transaction.getBase58Id()
  );

  var sig = sendTransactionFuture.join();
  System.out.println("Confirmed transaction " + sig);
}