blockchainswapsolana

Can Jupiter api swap and send swapped token to another account?


I am using Jupiter api for my app and on my app I want to swap and directly send the swapped token to merchant account. can I do this on one transaction?

https://station.jup.ag/docs/apis/payments-api

from this document I thought if I set destinationTokenAccount as merchant wallet address then the swapped token can send to the merchant account directly.

  // Replace with the Jupiter API endpoint
  const JUPITER_QUOTE_API = 'https://quote-api.jup.ag/v6/quote';
  const JUPITER_SWAP_API = 'https://quote-api.jup.ag/v6/swap';


  // Step 1: Fetch swap info from Jupiter
  const fetchSwapInfo = async (inputMint: string, outputMint: string, amount: number) => {
    const url = `${JUPITER_QUOTE_API}?inputMint=${inputMint}&outputMint=${outputMint}&amount=${amount}&swapMode=ExactOut&slippageBps=50`;
    const response = await fetch(url);
    const data = await response.json();
    return {
        inAmount: data.inAmount,
        otherAmountThreshold: data.otherAmountThreshold,
        quoteResponse: data,
    };
  };

  // Step 2: Fetch swap transaction from Jupiter
  const fetchSwapTransaction = async (
    walletAddress: PublicKey | null,
    recipientAddress: string,
    swapInfo: any
  ) => {
    if (!walletAddress || !recipientAddress || !swapInfo?.quoteResponse) {
        throw new Error('Invalid parameters: Ensure walletAddress, recipientAddress, and swapInfo are defined.');
    }

    const requestBody = {
        userPublicKey: walletAddress.toBase58(),
        destinationTokenAccount: recipientAddress,
        useSharedAccounts: true,
        quoteResponse: swapInfo.quoteResponse,
        skipUserAccountsRpcCalls: true,

    };

    const response = await fetch(JUPITER_SWAP_API, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`Error fetching swap transaction: ${errorText}`);
    }

    const { swapTransaction, lastValidBlockHeight } = await response.json();
    return { swapTransaction, lastValidBlockHeight };
  };

  // Step 3: Send transaction to Solana blockchain
  const sendTransaction = async (swapTransaction: string,  walletAdapter: WalletAdapter) => {
      const versionedMessage = VersionedMessage.deserialize(Buffer.from(swapTransaction, 'base64'));
      const transaction = new VersionedTransaction(versionedMessage);
      const bhInfo = await connection.getLatestBlockhashAndContext({ commitment: "finalized" });
      transaction.recentBlockhash = bhInfo.value.blockhash;
      transaction.feePayer = walletAdapter.publicKey;
      console.log(transaction);
      const signedTransaction = await (walletAdapter as any).signTransaction(transaction);
      
      const simulation = await connection.simulateTransaction(transaction, { commitment: "finalized" });
      if (simulation.value.err) {
        throw new Error(`Simulation failed: ${simulation.value.err.toString()}`);
      }

     // Send the transaction
  try {
    const signature = await connection.sendTransaction(transaction, {
      // NOTE: Adjusting maxRetries to a lower value for trading, as 20 retries can be too much
      // Experiment with different maxRetries values based on your tolerance for slippage and speed
      // Reference: https://solana.com/docs/core/transactions#retrying-transactions
      maxRetries: 5,
      skipPreflight: true,
      preflightCommitment: "finalized",
    });

    // Confirm the transaction
    // Using 'finalized' commitment to ensure the transaction is fully confirmed
    // Reference: https://solana.com/docs/core/transactions#confirmation
    const confirmation = await connection.confirmTransaction({
      signature,
      blockhash: bhInfo.value.blockhash,
      lastValidBlockHeight: bhInfo.value.lastValidBlockHeight,
    }, "finalized");

    if (confirmation.value.err) {
      throw new Error(`Transaction not confirmed: ${confirmation.value.err.toString()}`);
    }

      console.log("Confirmed: ", signature);
    } catch (error) {
      console.error("Failed: ", error);
      throw error;
    }
  };

  // Step 4: Main function to swap and send token
  const swapAndSendToken = async (
    walletAdapter: WalletAdapter,
    recipientAddress: string,
    inputMint: string,
    outputMint: string,
    amount: number
  ) => {
    try {
        const walletPublicKey = walletAdapter.publicKey;

        // Step 1: Fetch swap info
        const swapInfo = await fetchSwapInfo(inputMint, outputMint, amount);

        // Step 2: Fetch the swap transaction
        const { swapTransaction, lastValidBlockHeight } = await fetchSwapTransaction(walletPublicKey, recipientAddress, swapInfo);
        console.log(swapTransaction);
        // Step 3: Send the transaction to the blockchain
        await sendTransaction(swapTransaction,  walletAdapter);

        alert("USDC sent successfully!");

        console.log('Swap and send transaction completed successfully.');
    } catch (error) {
        console.error('Error during swap and send:', error);
        //alert("Failed! " + error.message);
    }
  };
  const payCoin = async () => {
    const wallets = [
      new PhantomWalletAdapter(),
      new SolflareWalletAdapter(),
      new TrustWalletAdapter(),
    ];
    if(connectedWalletIndex == null) return;
    const wallet = wallets[connectedWalletIndex];

      try {
        await wallet.connect();
        if(wallet.publicKey == null) return;
        console.log("Connected to wallet:", wallet.publicKey.toString());
        const publicKey = wallet.publicKey;

        await swapAndSendToken(
            wallet,
            "ANJt85VAVGhknPAhKBaS2qWVZUWW59rkQSbAg4sW4dFA", // Merchant's USDC address
            "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", // Input mint address
            "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // Output mint address
            0.1 * 1000000 // Example: 0.1 USDC in micro-lamports
        );
    } catch (error) {
        console.error("Failed to connect to wallet:", error);
    }
  }

This is my implemented code.

I am going to swap with destination address. I've seen destinationTokenAccount from the https://station.jup.ag/api-v6/post-swap-instructions Is it possible to swap from customer account to merchant address directly? I've tried but it's not succeed. any one can help me? https://solscan.io/tx/37GAcwzCfEbydZknATZXRMjDEzMPKV5b3TF3VtM2JUD5y6cKoKWipQD5qfpB2MLtmkDsLgVhzyWrU5kdHRi9HRqh This is the failed transaction.


Solution

  • For the 'destinationTokenAccount' check your property 'recipientAddress'. It must be Token Account. It is not wallet address for deposit. You can to get it with RPC method getTokenAccountsByOwner (@solana/web3.js) or getAssociatedTokenAddressSync (@solana/spl-token)