Skip to content

Commit

Permalink
Inspired by the transaction receipt synchronous blocking confirmation…
Browse files Browse the repository at this point in the history
… strategy in web3J, a synchronous blocking confirmation feature has been implemented in the current repository.
  • Loading branch information
juzi-code committed Dec 29, 2024
1 parent 7dbf1e0 commit 400a585
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/main/java/org/p2p/solanaj/rpc/RpcApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import org.p2p.solanaj.rpc.types.TokenResultObjects.TokenAmountInfo;
import org.p2p.solanaj.rpc.types.config.*;
import org.p2p.solanaj.rpc.types.config.RpcSendTransactionConfig.Encoding;
import org.p2p.solanaj.transaction.confirm.SolanaPollingTransactionConfirmProcessor;
import org.p2p.solanaj.transaction.confirm.SolanaTransactionConfirmProcessor;
import org.p2p.solanaj.transaction.exceptions.SolanaTransactionException;
import org.p2p.solanaj.ws.SubscriptionWebSocketClient;
import org.p2p.solanaj.ws.listeners.NotificationEventListener;

Expand Down Expand Up @@ -133,6 +136,24 @@ public void sendAndConfirmTransaction(Transaction transaction, List<Account> sig
subClient.signatureSubscribe(signature, listener);
}

/**
* Send the transaction and block the current thread until the transaction status is confirmed or the waiting time is exceeded.
* @param transaction the transaction to send
* @param signers the list of accounts that will sign the transaction
* @param confirmProcessor the processor that will process the transaction
* @return the confirmation transaction
* @throws RpcException if an error occurs during the RPC call
* @throws SolanaTransactionException Transaction confirmation failed or exceeded the maximum waiting time.
*/
public ConfirmedTransaction sendAndConfirmTransaction(Transaction transaction, List<Account> signers, SolanaTransactionConfirmProcessor confirmProcessor) throws RpcException, SolanaTransactionException {
String signature = sendTransaction(transaction, signers, null);
return confirmProcessor.waitForTransactionConfirm(signature);
}

public ConfirmedTransaction sendAndConfirmTransaction(Transaction transaction, List<Account> signers) throws RpcException, SolanaTransactionException {
return sendAndConfirmTransaction(transaction, signers, new SolanaPollingTransactionConfirmProcessor());
}

public void sendAndConfirmRawTransaction(String encodeSerializedTransaction, RpcSendTransactionConfig rpcSendTransactionConfig,
NotificationEventListener listener) throws RpcException {
String signature = sendRawTransaction(encodeSerializedTransaction, rpcSendTransactionConfig);
Expand All @@ -141,6 +162,15 @@ public void sendAndConfirmRawTransaction(String encodeSerializedTransaction, Rpc
subClient.signatureSubscribe(signature, listener);
}

public ConfirmedTransaction sendAndConfirmRawTransaction(String encodeSerializedTransaction, RpcSendTransactionConfig rpcSendTransactionConfig, SolanaTransactionConfirmProcessor confirmProcessor) throws RpcException, SolanaTransactionException {
String signature = sendRawTransaction(encodeSerializedTransaction, rpcSendTransactionConfig);
return confirmProcessor.waitForTransactionConfirm(signature);
}

public ConfirmedTransaction sendAndConfirmRawTransaction(String encodeSerializedTransaction, RpcSendTransactionConfig rpcSendTransactionConfig) throws RpcException, SolanaTransactionException {
return sendAndConfirmRawTransaction(encodeSerializedTransaction, rpcSendTransactionConfig, new SolanaPollingTransactionConfirmProcessor());
}

public long getBalance(PublicKey account) throws RpcException {
return getBalance(account, null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.p2p.solanaj.transaction.confirm;

import org.p2p.solanaj.rpc.Cluster;
import org.p2p.solanaj.rpc.RpcClient;
import org.p2p.solanaj.rpc.RpcException;
import org.p2p.solanaj.rpc.types.ConfirmedTransaction;
import org.p2p.solanaj.rpc.types.config.Commitment;
import org.p2p.solanaj.transaction.exceptions.SolanaTransactionException;


/**
* 使用每个提供的事务id,轮询直到我们获到确认交易
*/
public class SolanaPollingTransactionConfirmProcessor extends SolanaTransactionConfirmProcessor {
protected final long sleepDuration;
protected final int attempts;

public SolanaPollingTransactionConfirmProcessor(RpcClient rpcClient, Commitment commitment, long sleepDuration, int attempts) {
super(rpcClient, commitment);
this.sleepDuration = sleepDuration;
this.attempts = attempts;
}

public SolanaPollingTransactionConfirmProcessor(RpcClient rpcClient, Commitment commitment) {
this(rpcClient, commitment, 1000, 120);
}

public SolanaPollingTransactionConfirmProcessor(RpcClient rpcClient) {
this(rpcClient, Commitment.FINALIZED);
}

public SolanaPollingTransactionConfirmProcessor() {
this(new RpcClient(Cluster.MAINNET));
}

@Override
public ConfirmedTransaction waitForTransactionConfirm(String txid) throws SolanaTransactionException, RpcException {
return getTransactionReceipt(txid, sleepDuration, attempts);
}

private ConfirmedTransaction getTransactionReceipt(
String txid, long sleepDuration, int attempts)
throws SolanaTransactionException, RpcException {

for (int i = 0; i < attempts; i++) {
ConfirmedTransaction confirmedTransaction = sendConfirmTransactionRequest(txid);

if (confirmedTransaction != null) {
return confirmedTransaction;
}

// Sleep unless it is the last attempt.
if (i < attempts - 1) {
try {
Thread.sleep(sleepDuration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

throw new SolanaTransactionException(
"Transaction confirm was not generated after "
+ ((sleepDuration * attempts) / 1000
+ " seconds for txid: "
+ txid),
txid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.p2p.solanaj.transaction.confirm;

import org.p2p.solanaj.rpc.RpcClient;
import org.p2p.solanaj.rpc.RpcException;
import org.p2p.solanaj.rpc.types.ConfirmedTransaction;
import org.p2p.solanaj.rpc.types.config.Commitment;
import org.p2p.solanaj.transaction.exceptions.SolanaTransactionException;


/**
* solana交易状态确认检查处理
*/
public abstract class SolanaTransactionConfirmProcessor {
private final RpcClient rpcClient;
private final Commitment commitment;

public SolanaTransactionConfirmProcessor(RpcClient rpcClient, Commitment commitment) {
this.rpcClient = rpcClient;
this.commitment = commitment;
}

public abstract ConfirmedTransaction waitForTransactionConfirm(String txid)
throws SolanaTransactionException, RpcException;

ConfirmedTransaction sendConfirmTransactionRequest(String txid)
throws RpcException, SolanaTransactionException {
ConfirmedTransaction confirmedTransaction = rpcClient.getApi().getTransaction(txid, commitment);

if (confirmedTransaction == null) {
return null;
}
if (confirmedTransaction.getMeta().getErr() != null) {
throw new SolanaTransactionException(
"Error processing request: " + confirmedTransaction.getMeta().getErr());
}

return confirmedTransaction;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.p2p.solanaj.transaction.exceptions;


import lombok.Getter;

import java.util.Optional;

public class SolanaTransactionException extends Exception {

@Getter
private Optional<String> txid = Optional.empty();

public SolanaTransactionException(String message) {
super(message);
}

public SolanaTransactionException(String message, String txid) {
super(message);
this.txid = Optional.ofNullable(txid);
}

public SolanaTransactionException(Throwable cause) {
super(cause);
}


}

0 comments on commit 400a585

Please sign in to comment.