jup-ag / jupiter-core-example

94 stars 57 forks source link

Unexpected timeouts while using jup.ag swap api in NodeJS #13

Open Jandzi opened 9 months ago

Jandzi commented 9 months ago

Hi guys, I just started to use the Jupiter APIs, but I have one problem with them. It is terribly slow. The code does work, it will swap, but 9 out of 10 times it will time out and I don't know why. I tried to find out where the code gets stuck and it is on the following code: await connection.confirmTransaction(txid); Why could that be? I have also used different RPC endpoints, for example the Helius one, and it was still super slow. I have also made a "race" between trying to swap in in the Jupiter GUI using my custom Helios RPC endpoint and my code with the same Helios RPC endpoint and the GUI was significantly faster. What am I missing please? This is the code:

const { Connection, Keypair, VersionedTransaction } = require('@solana/web3.js');
const fetch = require('cross-fetch');
const bs58 = require('bs58');

const private_key = 'YOUR_PRIVATE_KEY';
const inputMint = 'So11111111111111111111111111111111111111112';
const outputMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
const amount = 100000;
const slippageBps = 10;
const rpcEndpoint = 'https://api.mainnet-beta.solana.com';

//setup connection
const connection = new Connection(rpcEndpoint);

// Setup wallet
const secretKey = bs58.decode(private_key);
const wallet = Keypair.fromSecretKey(secretKey);

async function swap() {
    try {
        // Get quote
        const quoteResponse = await (
            await fetch(`https://quote-api.jup.ag/v6/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${amount}&slippageBps=${slippageBps}`)
        ).json();

        // Get serialized transactions for the swap
        const { swapTransaction } = await (
                await fetch('https://quote-api.jup.ag/v6/swap', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    quoteResponse,
                    userPublicKey: wallet.publicKey.toString(),
                    wrapAndUnwrapSol: true,
                    dynamicComputeUnitLimit: true,
                    prioritizationFeeLamports: 'auto'
                })
            })
        ).json();

        // Deserialize and sign the transaction
        const swapTransactionBuf = Buffer.from(swapTransaction, 'base64');
        const transaction = VersionedTransaction.deserialize(swapTransactionBuf);
        transaction.sign([wallet]);

        // Execute the transaction
        const rawTransaction = transaction.serialize();
        const txid = await connection.sendRawTransaction(rawTransaction, {
            skipPreflight: true,
            maxRetries: 2
        });
        await connection.confirmTransaction(txid);

        console.log(`Swap successful! Transaction ID: ${txid}`);
        console.log(`View the transaction on Solscan: https://solscan.io/tx/${txid}`);
    } catch (error) {
        console.error('Error during swap:', error);
    }
}

// Run the swap function
swap();

Thank you for you time!

zerotop commented 9 months ago

For me it helped to wait 5 seconds and use another way to validate the transaction:

await new Promise(r => setTimeout(r, 5000));

 const result = await connection.getSignatureStatus(txid, {
    searchTransactionHistory: true,
  });
  console.log(result);
zerotop commented 9 months ago

An even better addition:

// Execute the transaction
var tryAgain=true;
var objSignatureStatusResult;
var maxTriesCounter=0; 
var maxTries=5; 

while (tryAgain) {
    maxTriesCounter++;
    const rawTransaction = transaction.serialize()
    const txid = await connection.sendRawTransaction(rawTransaction, {
      skipPreflight: true,
      maxRetries: 2
    });

    console.log(`https://solscan.io/tx/${txid}`);
    await new Promise(r => setTimeout(r, 1500));

    const result = await connection.getSignatureStatus(txid, {
        searchTransactionHistory: true,
        });
    objSignatureStatusResult = JSON.parse(JSON.stringify(result));
    if ( objSignatureStatusResult.value !== null) tryAgain=false;
    if (maxTriesCounter>maxTries) tryAgain=false;
}
Marijus commented 8 months ago

I'm facing the exact same issue. If I try to swap a less busy token - it works almost every time, however, with busier tokens it rarely succeeds. Did you ever find a solution to the problem?

Jandzi commented 8 months ago

An even better addition:

// Execute the transaction
var tryAgain=true;
var objSignatureStatusResult;
var maxTriesCounter=0; 
var maxTries=5; 

while (tryAgain) {
  maxTriesCounter++;
  const rawTransaction = transaction.serialize()
  const txid = await connection.sendRawTransaction(rawTransaction, {
    skipPreflight: true,
    maxRetries: 2
  });

  console.log(`https://solscan.io/tx/${txid}`);
  await new Promise(r => setTimeout(r, 1500));

  const result = await connection.getSignatureStatus(txid, {
      searchTransactionHistory: true,
      });
  objSignatureStatusResult = JSON.parse(JSON.stringify(result));
  if ( objSignatureStatusResult.value !== null) tryAgain=false;
  if (maxTriesCounter>maxTries) tryAgain=false;
}

You are a life saver, thank you very much! Everything works now!

Jandzi commented 8 months ago

I'm facing the exact same issue. If I try to swap a less busy token - it works almost every time, however, with busier tokens it rarely succeeds. Did you ever find a solution to the problem?

Yes, as zerotop pointed out, you have to add the while loop, that basically retries to executing the transaction.