I am trying to send spl tokens and make it consistent despite this congestion. I’ve been getting 2 successful transactions in 50 tries even though I am using a function from Jupiter that forcefully sends transactions multiple times.
I have seen the likes of Phantom wallet sending SPL tokens without failing. So, I believe it is possible. I have upped the retries to 100,000 even. It still didn’t guarantee consistency. Is there any suggestion?
Here is my code
const {
Connection,
Keypair,
VersionedTransaction,
PublicKey,
TransactionExpiredBlockheightExceededError,
VersionedTransactionResponse,
Transaction
} = require("@solana/web3.js");
const {
createTransferInstruction,
getAssociatedTokenAddress,
TOKEN_PROGRAM_ID,
getOrCreateAssociatedTokenAccount
} = require('@solana/spl-token');
const fetch = require("cross-fetch");
const { Wallet } = require("@project-serum/anchor");
const bs58 = require("bs58");
const promiseRetry = require("promise-retry");
async function getWallet(privateKeys) {
return new Wallet(Keypair.fromSecretKey(bs58.decode(privateKeys)))
}
const QUICKNODE_RPC ="https://mainnet.helius-rpc.com/";
const connection = new Connection(QUICKNODE_RPC, "confirmed");
const wait = (time) =>
new Promise((resolve) => setTimeout(resolve, time));
async function transactionSenderAndConfirmationWaiter({
connection,
serializedTransaction,
blockhashWithExpiryBlockHeight,
}) {
const txid = await connection.sendRawTransaction(
serializedTransaction,
{ skipPreflight: true }
);
const controller = new AbortController();
const abortSignal = controller.signal;
const abortableResender = async () => {
while (true) {
await wait(2_000);
if (abortSignal.aborted) return;
try {
await connection.sendRawTransaction(
serializedTransaction,
{ skipPreflight: true }
);
} catch (e) {
console.warn(`Failed to resend transaction: ${e}`);
}
}
};
try {
abortableResender();
const lastValidBlockHeight =
blockhashWithExpiryBlockHeight.lastValidBlockHeight - 150;
await Promise.race([
connection.confirmTransaction(
{
...blockhashWithExpiryBlockHeight,
lastValidBlockHeight,
signature: txid,
abortSignal,
},
"confirmed"
),
new Promise(async (resolve) => {
while (!abortSignal.aborted) {
await wait(2_000);
const tx = await connection.getSignatureStatus(txid, {
searchTransactionHistory: false,
});
if (tx?.value?.confirmationStatus === "confirmed") {
resolve(tx);
}
}
}),
]);
} catch (e) {
if (e instanceof TransactionExpiredBlockheightExceededError) {
console.log("TransactionExpiredBlockheightExceededError")
return null;
} else {
throw e;
}
} finally {
controller.abort();
}
const response = promiseRetry(
async (retry) => {
const response = await connection.getTransaction(txid, {
commitment: "confirmed",
maxSupportedTransactionVersion: 0,
});
if (!response) {
retry(response);
}
return response;
},
{
retries: 100000,
minTimeout: 1e3,
}
);
return response;
}
const sendSplTokenWithBase58 = async (
privateKeyBase58,
toKeyBase58,
numOfToken,
tokenAddress,
connection,
decimal = 6
) => {
try {
console.log("started");
const privateKey = bs58.decode(privateKeyBase58);
const fromKeyPair = Keypair.fromSecretKey(privateKey);
const mintAddress = new PublicKey(tokenAddress);
const tokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
fromKeyPair,
mintAddress,
fromKeyPair.publicKey,
);
const destinationAccount = await getOrCreateAssociatedTokenAccount(
connection,
fromKeyPair,
mintAddress,
new PublicKey(toKeyBase58),
);
const transaction = new Transaction({ feePayer: fromKeyPair.publicKey })
.add(
createTransferInstruction(
tokenAccount.address,
destinationAccount.address,
fromKeyPair.publicKey,
numOfToken * Math.pow(10, decimal),
),
);
const latestBlockHash = await connection.getLatestBlockhash("confirmed");
transaction.recentBlockhash = latestBlockHash.blockhash;
// Sign the transaction
transaction.sign(fromKeyPair);
// Serialize the transaction
const serializedTransaction = transaction.serialize();
// Get blockhash with expiry block height
const blockhashWithExpiryBlockHeight = {
blockhash: latestBlockHash.blockhash,
lastValidBlockHeight: latestBlockHash.lastValidBlockHeight
};
// Send and confirm the transaction using the new function
const transactionResponse = await transactionSenderAndConfirmationWaiter({
connection,
serializedTransaction,
blockhashWithExpiryBlockHeight,
});
// Extract relevant information from the transaction response
const transactionHash = transactionResponse?.transaction?.signatures[0].toBase58();
const transactionStatus = transactionResponse ? true : false;
const splTokensSent = numOfToken;
console.log({ transactionHash, transactionStatus, splTokensSent });
return { transactionHash, transactionStatus, splTokensSent };
} catch (error) {
console.error("Error occurred:", error);
return { transactionHash: false };
}
};
The original example from the transaction sender here https://github.com/jup-ag/jupiter-quote-api-node/blob/main/example/utils/transactionSender.ts uses 5 retries. I have tampered with it and tried 50, 1000, 10000, 100000. Everytime, very few transaction goes through and others fail
I also tried increasing compute fee by tampering with this part.
const txid = await connection.sendRawTransaction(
serializedTransaction,
{ skipPreflight: true, maxRetries: 5, priorityFeePerComputeUnit: 25000 } // Increased fee and added maxRetries
);
Can anyone help me?