Integrating limit order v2
Jupiter Limit Order v2 (LO) provides users with the simplest way to place limit orders on Solana and receive tokens directly in your wallet when the order is filled.
This page will serve as a general guide on integrating LO into your use case. whether you are building a bot for yourself or looking to integrate with existing (d)apps, Jupiter's LO program will work regardless of programming language used.
Learn more about the mechanics of Jupiter's LO here
Start using Limit Order APIs: here
Program Address
LO v2 Program (mainnet-beta):j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X
Create Limit Order API Walkthrough
This guide will show you how to create limit orders using Jupiter APIs. It also applies to canceling orders.
Install Required Libraries
We recommend using NodeJS >= 18 for the native fetch
API
bun install @solana/web3.js bs58
We recommend **NodeJS >= 18** for native `fetch` support.
You can use any of these package managers:
- **bun**
- **npm**
- **pnpm**
- **yarn**
```bash
# bun has built-in dotenv support: https://bun.sh/docs/runtime/env
bun install @solana/web3.js bs58
Import Libraries and Set Up RPC Connection
import {
Connection,
Keypair,
SendOptions,
VersionedTransaction,
} from "@solana/web3.js";
import bs58 from "bs58";
const RPC_URL = process.env.RPC_URL;
if (!RPC_URL) throw "missing RPC_URL env var";
const RPC_CONNECTION = new Connection(RPC_URL);
For TypeScript, these are the request body and response types that can be used.
type CreateOrder = {
inputMint: string;
outputMint: string;
maker: string;
payer: string;
params: {
makingAmount: string;
takingAmount: string;
expiredAt?: string;
feeBps?: string;
};
computeUnitPrice: string | "auto";
referral?: string;
inputTokenProgram?: string;
outputTokenProgram?: string;
wrapAndUnwrapSol?: boolean;
};
type CreateOrderResponse = {
order: string;
tx: string;
};
Set Up Your Wallet
Set up your wallet by decoding the private key from a base-58 string. Do not use private keys directly in production.
const WALLET_PRIV_KEY = process.env.WALLET_PRIV_KEY;
if (!WALLET_PRIV_KEY) throw "missing WALLET_PRIV_KEY";
const wallet = Keypair.fromSecretKey(bs58.decode(WALLET_PRIV_KEY));
Get the serialized transactions to create the limit order
Here we will do the following:
- Create the request body,
- Submit it to the API server,
- Deserialize the transaction from the response, and
- Sign and submit the transaction on chain
Do note that steps 2-4 are asynchronous and may reject. It is recommended to wrap this section in a
try-catch
block and handle the errors accordingly.
Creating the request body
In this example, we are creating a limit order to buy 100 USDC for 0.05 SOL with no expiry and using auto
for the computeUnitPrice
. For advanced users, you may specify a numerical computeUnitPrice
.
const createOrderBody: CreateOrder = {
maker: wallet.publicKey.toBase58(),
payer: wallet.publicKey.toBase58(),
inputMint: "So11111111111111111111111111111111111111112",
outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
params: {
makingAmount: "50000000", // 0.05 SOL
takingAmount: "100000000", // 100 USDC
},
// "auto" sets the priority fee based on network congestion
// and it will be capped at 500,000
computeUnitPrice: "auto",
};
Send request to API server
Ensure that the method of the request is POST
and that the body of the request is a stringified CreateOrder
type.
const fetchOpts: RequestInit<RequestInitCfProperties> = {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(createOrderBody),
};
const response = await fetch(
"https://api.jup.ag/limit/v2/createOrder",
fetchOpts
);
Sign and submit the transaction on chain
Sending the transaction on chain does NOT mean that the transaction is successful. We recommend using the hash returned from RPC_CONNECTION.sendRawTransaction()
and:
- Check if the hash appears on an explorer, or
- Use
RPC_CONNECTION.confirmTransaction()
Note that transactions may not appear on chain for some time. We recommend retrying (repeat the entire flow) only if the transaction has landed on chain but has failed (likely due to an invalid input), or if the transaction does not appear on chain after ~2 minutes*.
// Deserialise base64 tx response
const { order, tx } = await response.json<CreateOrderResponse>();
const txBuff = Buffer.from(tx, "base64");
const vtx = VersionedTransaction.deserialize(txBuff);
// Sign with wallet
vtx.sign([wallet]);
const rpcSendOpts: SendOptions = { skipPreflight: true };
const hash = await RPC_CONNECTION.sendRawTransaction(
vtx.serialize(),
rpcSendOpts
);