Swap API and SDK

Dedicated Nodes can optionally use either the Hello Moon Swap API or Jupiter Swap API plug-in. When enabled, this allows you to build transactions and get on-chain prices for any token on major DEXs with slippage protection. Select your preferred Swap API plug-in when you purchase a dedicated node to use it. You then interact with it via HTTP requests or using an SDK. The two Swap APIs are similar but have some differences. The Hello Moon Swap API, for example, allows you to get a quote and submit a transaction in one call, where the Jupiter API requires two calls to do the same.

Swap API Options

Hello Moon Swap API

Using the SDK

Our Typescript SDK for the Hello Moon Swap API is hosted at https://www.npmjs.com/package/@hellomoon/swap-api. It has examples of how to create a connection, get a swap quote, and execute a swap transaction.

Connecting to the Swap API

The Hello Moon Swap API is hosted on your dedicated node at the base URL of <RPC_URL>/api/swap/. Use this base URL with the endpoints below.

Authorization

The Hello Moon Swap API routes can be accessed with your RPC token in the authorization header of the POST request. For example if your dedicated node RPC URL is https://raging-river.hellomoon.io/hf0e87h7ewh98 you would set an authorization header like { Authorization: "Bearer hf0e87h7ewh98" } on all routes.

Get a quote

You can get a quote for a token swap by sending a POST request to the /get_quote endpoint of the Hello Moon Swap API.

The parameters it takes are:

tokenIn: string; // public key of the token you are providing
tokenOut: string; // public key of the token you want to swap for
action: Action; // Set to 'exactIn' or 'exactOut'
amount: string; // Amount specified in Lamports
slippageBps?: number; // Slippage Basis Points. Optional.
maxHops?: number; // Optional.
exemptFees?: boolean; // Optional.

Response

This will return a response in the form of:

type GetQuoteResponse = {
  aggregatorFee: string; // u64 as string
  slot: string; // u64 as string
  timestamp: string; // u64 as string
  path: GenericQuote[]; // Vec of quotes
  minAmountOutOrMaxIn: string; // u64 as string
};

type GenericQuote = {
  pool: string;
  swapContext: any; // This might need a more specific type depending on your needs
  direction: string;
  amountIn: string; // u64 as string
  amountOut: string; // u64 as string
  feeTaken: string; // u64 as string
  token0Impact: string; // Bps as decimal string
  token1Impact: string; // Bps as decimal string
};

Create a versioned transaction

Get a versioned transaction by sending a POST request to the /get_transaction endpoint with the following parameters

tokenIn: string; // public key of the token you are providing
tokenOut: string; // public key of the token you want to swap for
action: Action; // set to 'exactIn' or 'exactOut'
amount: string; // amount in Lamports
userPubkey: string; // public key of your wallet
maxHops?: number; // optional since it has a default
slippageBps?: number; // optional since it has a default

Response

This will return a response in the form:

txn: string; // this is the versioned transaction

Execute the versioned transaction

Once you have a versioned transaction, you need to execute it. This is not specific to the Swap API and you can do this using any library. An example of how to do this in TypeScript might look like:

// Import the libraries we need
import { Connection, VersionedTransaction } from "@solana/web3.js";
import bs58 from "bs58";

// Assume you already have these variables set up
conn = // this is a connection to your RPC
  versionedTxn = // this is the versioned transaction returned from the Swap API that you want to execute
  // Deserialize the transaction
  txn =
    VersionedTransaction.deserialize(bs58.decode(versionedTxn));

// Sign the transaction with your wallet
txn.sign([keyPair]);

// Simulate the transation
const sim = await conn.simulateTransaction(txn);

// If there are any problems, bail out
if (sim.value.err) {
  console.error(sim.value.err);
  console.error(sim.value.logs);
  throw new Error("Transaction Sim Response resulted in error");
}

// Send the transaction!
await conn.sendRawTransaction(txn.serialize());

Jupiter Swap API

Using the SDK

Our Jupiter Swap API integration uses the official Jupiter API package available at https://www.npmjs.com/package/@jup-ag/api. This package provides simple methods to retrieve quotes and execute swap transactions.

Connecting to the Jupiter Swap API

The Jupiter Swap API is accessible through your dedicated node at the base URL of <RPC_URL>/api/jup/. Use this base URL with the endpoints described below.

Authorization

Jupiter Swap API routes use the same authorization method as the Hello Moon Swap API. Include your RPC token in the authorization header of your POST requests. For example, if your dedicated node RPC URL is https://raging-river.hellomoon.io/hf0e87h7ewh98, set an authorization header like { Authorization: "Bearer hf0e87h7ewh98" } on all routes.

Get a quote

You can retrieve a quote for a token swap by using the quoteGet method from the Jupiter SDK.

Example Using the SDK

import { createJupiterApiClient } from "@jup-ag/api";
import { inspect } from "util";

const main = async () => {
  const jupiter = createJupiterApiClient({
    basePath: "https://raging-river.hellomoon.io/api/jup",
    headers: {
      Authorization: "Bearer hf0e87h7ewh98",
    },
  });

  const quote = await jupiter.quoteGet({
    inputMint: "So11111111111111111111111111111111111111112", // SOL
    outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
    amount: 100000000, // 0.1 SOL (100000000 lamports)
    slippageBps: 100, // 1% slippage tolerance
  });

  console.log(inspect(quote));
};

if (require.main === module) {
  main().catch(console.error);
}

Response

This will return a response in the following format:

{
  inputMint: 'So11111111111111111111111111111111111111112',
  inAmount: '100000000',
  outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  outAmount: '13778298',
  otherAmountThreshold: '13640516',
  swapMode: 'ExactIn',
  slippageBps: 100,
  platformFee: undefined,
  priceImpactPct: '0',
  routePlan: [
    { swapInfo: [Object], percent: 100 },
    { swapInfo: [Object], percent: 100 }
  ],
  contextSlot: 329349748,
  timeTaken: 0.002020169
}

Create a swap transaction

You can create a swap transaction by using the swapPost method from the Jupiter SDK:

import { createJupiterApiClient } from "@jup-ag/api";
import { Connection, Keypair, VersionedTransaction } from "@solana/web3.js";
import bs58 from "bs58";
import { inspect } from "util";

// Replace with your actual private key
const BS58_PRIVATE_KEY = "YOUR_PRIVATE_KEY";

const main = async () => {
  // Initialize Jupiter API client
  const jupiter = createJupiterApiClient({
    basePath: "https://raging-river.hellomoon.io/api/jup",
    headers: {
      Authorization: "Bearer hf0e87h7ewh98",
    },
  });

  // Step 1: Get a quote
  const quote = await jupiter.quoteGet({
    inputMint: "So11111111111111111111111111111111111111112", // SOL
    outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
    amount: 100000000, // 0.1 SOL (100000000 lamports)
    slippageBps: 100, // 1% slippage tolerance
  });

  console.log("Quote obtained:", inspect(quote, { depth: null }));

  // Step 2: Create a wallet for transaction (replace with your actual wallet)
  // Note: In production, use a more secure way to handle private keys
  const privateKey = bs58.decode(BS58_PRIVATE_KEY);
  const wallet = Keypair.fromSecretKey(privateKey);

  // Step 3: Connect to Solana
  const connection = new Connection(
    "https://raging-river.hellomoon.io/hf0e87h7ewh98",
    "confirmed",
  );

  // Step 4: Create the swap transaction
  const swapTransaction = await jupiter.swapPost({
    swapRequest: {
      quoteResponse: quote,
      userPublicKey: wallet.publicKey.toString(),
    },
  });

  console.log(
    "Swap transaction created:",
    inspect(swapTransaction, { depth: null }),
  );

  // Step 5: Deserialize the transaction
  const swapTransactionBuf = Buffer.from(
    swapTransaction.swapTransaction,
    "base64",
  );
  const transaction = VersionedTransaction.deserialize(swapTransactionBuf);

  // Step 6: Sign the transaction
  transaction.sign([wallet]);

  // Step 7: Execute the transaction
  const signature = await connection.sendTransaction(transaction);
  console.log("Transaction submitted:", signature);

  // Step 8: Confirm the transaction
  const confirmation = await connection.confirmTransaction(
    signature,
    "confirmed",
  );
  console.log("Transaction confirmed:", confirmation);
};

if (require.main === module) {
  main().catch(console.error);
}

Response

The output from this sample will look something like the following:

Quote obtained: {
  inputMint: 'So11111111111111111111111111111111111111112',
  inAmount: '100000000',
  outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  outAmount: '13949101',
  otherAmountThreshold: '13809610',
  swapMode: 'ExactIn',
  slippageBps: 100,
  platformFee: undefined,
  priceImpactPct: '0',
  routePlan: [
    {
      swapInfo: {
        ammKey: '71GHcjkwmtM7HSWBuqzjEp96prcNNwu1wpUywXiytREU',
        label: 'Lifinity V2',
        inputMint: 'So11111111111111111111111111111111111111112',
        outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
        inAmount: '100000000',
        outAmount: '13949101',
        feeAmount: '0',
        feeMint: 'So11111111111111111111111111111111111111112'
      },
      percent: 100
    }
  ],
  contextSlot: 329537341,
  timeTaken: 0.002056727
}
Swap transaction created: {
  swapTransaction: '<redacted>',
  lastValidBlockHeight: 307787472,
  prioritizationFeeLamports: 99999
}
Transaction submitted: <redacted>
Transaction confirmed: { context: { slot: 329537344 }, value: { err: null } }