0

I use Jupiter V6 swap request to get a post request and it works well. My question is hot to add 1 more instruction with SOL transfer to my fee address? 0.1% of SOL for example

I've been trying to add it for a long hours and I haven't been successful, so I ask you to help me. Please give me any advice or tell me what am I doing wrong. Maybe I should use /swap-instructions instead? Or I should receive legacyTransaction and then just add 1 more instruction. Please help

const fetch = require('cross-fetch');
const { Wallet } = require('@project-serum/anchor');
const bs58 = require('bs58');
const { createJupiterApiClient } = require('@jup-ag/api');
const jupiterQuoteApi = createJupiterApiClient();
const transactionSenderAndConfirmationWaiter = require("../../utils/transactionSender.js").transactionSenderAndConfirmationWaiter;
const getSignature = require("../../utils/getSignature.js").getSignature;
const { feeAddress } = require('../../main/variables.js');
const feePublicKey = new PublicKey(feeAddress)



async function swapSolToToken(connection, requestBody) {

    const wallet = new Wallet(Keypair.fromSecretKey(bs58.decode(requestBody.privateKey)));

    const realSolAmount = requestBody.solAmount * 1000000000

    // Swapping SOL to Token quote
    const quoteResponse = await jupiterQuoteApi.quoteGet({
        inputMint: "So11111111111111111111111111111111111111112",
        outputMint: requestBody.tokenMint,
        amount: realSolAmount,
        // platformFeeBps: 10,
        // asLegacyTransaction: true, // legacy transaction, default is versoined transaction
        slippageBps: 2500
    });

    if (!quoteResponse) {
        console.error("unable to quote");
        return;
    }


    const swapResult = await jupiterQuoteApi.swapPost({
        swapRequest: {
        // quoteResponse from /quote api
        quoteResponse: quoteResponse,
        // user public key to be used for the swap
        userPublicKey: wallet.publicKey.toBase58(),
        // auto wrap and unwrap SOL. default is true
        wrapAndUnwrapSol: true,
        dynamicComputeUnitLimit: true,
        prioritizationFeeLamports: "auto",
        // prioritizationFeeLamports: {
        //   autoMultiplier: 2,
        // },
        // feeAccount is optional. Use if you want to charge a fee.  feeBps must have been passed in /quote API.
        // feeAccount: "fee_account_public_key"
        },
    });

    // deserialize the transaction
    const swapTransactionBuf = Buffer.from(swapResult.swapTransaction, 'base64');
    var transaction = VersionedTransaction.deserialize(swapTransactionBuf);
    console.log(transaction);

    // sign the transaction
    transaction.sign([wallet.payer]);
    const signature = getSignature(transaction);

    // We first simulate whether the transaction would be successful
    const { value: simulatedTransactionResponse } =
        await connection.simulateTransaction(transaction, {
        replaceRecentBlockhash: true,
        commitment: "processed",
        });
    const { err, logs } = simulatedTransactionResponse;

    if (err) {
        // Simulation error, we can check the logs for more details
        // If you are getting an invalid account error, make sure that you have the input mint account to actually swap from.
        console.error("Simulation Error:");
        console.error({ err, logs });
        return;
    }

    const serializedTransaction = Buffer.from(transaction.serialize());
    const blockhash = transaction.message.recentBlockhash;

    const transactionResponse = await transactionSenderAndConfirmationWaiter({
        connection,
        serializedTransaction,
        blockhashWithExpiryBlockHeight: {
        blockhash,
        lastValidBlockHeight: swapResult.lastValidBlockHeight,
        },
    });

    // If we are not getting a response back, the transaction has not confirmed.
    if (!transactionResponse) {
        console.error("Transaction not confirmed");
        return;
    }

    if (transactionResponse.meta?.err) {
        console.error(transactionResponse.meta?.err);
    }

    console.log(`https://solscan.io/tx/${signature}`);

    return signature
}```

2 Answers 2

1

So what Jup.ag is doing when you call it for a quote is providing you with the instructions itself to conduct the Swap, if you want to send a fee back to yourself or your fee address you can do so by creating a additional transfer instruction on top of this.

    // deserialize the transaction
    const swapTransactionBuf = Buffer.from(swapResult.swapTransaction, 'base64');
    var transaction = VersionedTransaction.deserialize(swapTransactionBuf);

    // Create the transfer instruction for the fee
    const transferInstruction = SystemProgram.transfer({
        fromPubkey: wallet.publicKey,
        toPubkey: feePublicKey,
        lamports: feeAmount
    });

    transaction.add(transferInstruction);

The SystemProgram.transfer function in Solana's framework is used to create a transaction instruction that facilitates the transfer of SOL (Solana's native cryptocurrency) from one account to another.

https://solana.com/docs/core/accounts#system-program

https://solana-labs.github.io/solana-web3.js/classes/SystemProgram.html#transfer

Update:

VersionedTransactions does not have the .add method so a different approach needs to be followed but in the end we will still be adding the same instruction onto the transaction as my first responce.

    // deserialize the transaction
    const swapTransactionBuf = Buffer.from(swapResult.swapTransaction, 'base64');
    var transaction = VersionedTransaction.deserialize(swapTransactionBuf);

    // Create the transfer instruction for the fee
    const transferInstruction = SystemProgram.transfer({
        fromPubkey: wallet.publicKey,
        toPubkey: feePublicKey,
        lamports: feeAmount
    });

    const addressLookupTableAccounts = await Promise.all(
      transaction.message.addressTableLookups.map(async (lookup) => {
        return new AddressLookupTableAccount({
          key: lookup.accountKey,
          state: AddressLookupTableAccount.deserialize(await connection.getAccountInfo(lookup.accountKey).then((res) => res.data)),
        });
      })
    );

    var message = TransactionMessage.decompile(transaction.message, {addressLookupTableAccounts: addressLookupTableAccounts});
    message.instructions.push(transferInstruction);
    transaction.message = message.compileToV0Message(addressLookupTableAccounts);

From here you continue to sign and send the message, as per the Jup.ag docs we then have to fetch the Address Lookup Table accounts which is needed to decompile and compile the transaction message.

We then proceed to decompile the original transaction message, add your new instructions, and then recompile it to include these changes.

and from there you continue signing and sending the message

https://docs.jup.ag/docs/additional-topics/composing-with-versioned-transaction

Update 2:

Was having fun looking at the Jup.ag docs and noticed a page building off of what you where trying to do initially so I will be posting the code for this as well.

// Define the referral account public key (obtained from the referral dashboard)
const referralAccountPublicKey = new PublicKey("your_referral_account_public_key_here");

// Define the fee token account (use the correct mint according to your swap's input or output mint)
const feeTokenAccount = await PublicKey.findProgramAddressSync(
    [Buffer.from("referral_ata"), referralAccountPublicKey.toBuffer(), requestBody.tokenMint.toBuffer()],
    new PublicKey("REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3")
);

// Get a quote with platform fee included
const quoteResponse = await jupiterQuoteApi.quoteGet({
    inputMint: "So11111111111111111111111111111111111111112",
    outputMint: requestBody.tokenMint,
    amount: requestBody.solAmount * 1000000000, // Real SOL amount in lamports
    slippageBps: 2500,
    platformFeeBps: 20 // Set your platform fee here in basis points
});

if (!quoteResponse) {
    console.error("unable to quote");
    return;
}

// Execute the swap with the fee account specified
const swapResult = await jupiterQuoteApi.swapPost({
    swapRequest: {
        quoteResponse: quoteResponse, // Quote response from /quote API
        userPublicKey: wallet.publicKey.toBase58(), // User's public key for the swap
        wrapAndUnwrapSol: true, // Auto wrap and unwrap SOL if necessary
        dynamicComputeUnitLimit: true, // Dynamically adjust compute units
        prioritizationFeeLamports: "auto", // Optionally set prioritization fees
        feeAccount: feeTokenAccount[0].toBase58() // Specify the fee token account
    },
});

// Deserialize the received transaction
const swapTransactionBuf = Buffer.from(swapResult.swapTransaction, 'base64');
var transaction = VersionedTransaction.deserialize(swapTransactionBuf);

// Sign and send the transaction
transaction.sign([wallet.payer]); // Ensure the wallet is properly set up and can sign transactions
const rawTransaction = transaction.serialize();
const txid = await sendAndConfirmRawTransaction(connection, Buffer.from(rawTransaction), {
    skipPreflight: true,
    commitment: 'confirmed',
    maxRetries: 2
});
console.log(`Transaction successful: https://solscan.io/tx/${txid}`);

Here you need to ensure that your Referral Account has been created with Jup.ag in which the feeAccount will be derived from and matches the mint of the swap's output (for ExactIn) or input (for ExactOut).

platformFeeBps needs to be included in /quote API call and when calling the /swap endpoint, the feeAccount needs to be included there as well.

It is important to note that utilizing this approach that Jup.ag will take a 2.5% cut from the fee's you are collecting as well.

https://docs.jup.ag/docs/apis/adding-fees

7
  • Hello, thank you for your assistance! Unfortunately, I've tried this and it didn't work. Am I doing something wrong? I import SystemProgram from web3.js const { VersionedTransaction, SystemProgram } = require('@solana/web3.js'); And I get this error Error handling request: TypeError: transaction.add is not a function Commented Apr 26 at 17:23
  • As I understand, VersionedTransaction doesn't have method .add Commented Apr 26 at 17:29
  • Ahh thanks for the catch, I was going off my prior experience of working with normal transactions and totally missed that VersionedTransactions do not have this method. As per the answer I posted you would follow the same approach of constructing an additional instruction, I will update the answer to include handling of VersionedTransactions @AndrewSwift Commented Apr 26 at 18:21
  • @AndrewSwift I have provided the necessary updates to handle VersionedTransactions and the process to decompile and recompile the transactions to be able to add additional instructions. Please take a look. docs.jup.ag/docs/additional-topics/… Commented Apr 26 at 18:29
  • 1
    I have added another update which builds on your original approach as I was reading the docs some more for fun and came across it, it is important to note that there are two approaches that you can utilize for this and that if you set a platform fee via Jups.ag API they will take a 2.5% cut from the fee's collected. It is up to you if you want to utilize Jups.ag API to set the platform fee or manually modify the transaction yourself and avoid this fee. @AndrewSwift Commented Apr 26 at 19:09
0

I'm in the https://referral.jup.ag/dashboard online access to the recommended account key, But solana's browser continues to display errors. The program expected this account to be already initializedenter image description here

enter image description here

url:https://solscan.io/tx/35BEwo7ca9vB8cQkYHZPw1PWNFiu3mxDoTcnesPGK37WVJjWcExWYX1YVf9JTwXN571TKWro6Uj2xupZivDxd8Wm thank you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.