Skip to main content
added 2948 characters in body
Source Link

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

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

added 1727 characters in body
Source Link

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:

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

Source Link

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