> ## Documentation Index
> Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing code demos

> TypeScript examples: SDK-based multi-hop swap through the router, and raw instruction building.

## Version info

* **SDK:** `@raydium-io/raydium-sdk-v2@0.2.42-alpha`
* **Network:** mainnet-beta
* **Router program ID:** `routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS`
* **Verified:** April 2026

## Example 1: SDK-based routing

Source: [`src/trade/routeSwap.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/trade/routeSwap.ts)

The Raydium SDK abstracts route building. Use the SDK's trade functions to compose a multi-hop route and execute it through the router automatically.

### Setup

```typescript theme={null}
import {
  Raydium,
  Router,
  Token,
  TokenAmount,
  toApiV3Token,
  toFeeConfig,
  TxVersion,
} from "@raydium-io/raydium-sdk-v2";
import {
  Connection,
  Keypair,
  PublicKey,
} from "@solana/web3.js";
import { NATIVE_MINT, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";

const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed");
const wallet = Keypair.fromSecretKey(/* your key bytes */);

const raydium = await Raydium.load({
  connection,
  owner: wallet,                 // the SDK will sign with this Keypair
  cluster: "mainnet",
  disableFeatureCheck: true,
  blockhashCommitment: "finalized",
});

// The router needs a chain-time anchor for CLMM hops.
await raydium.fetchChainTime();
```

### Build a multi-hop swap

Routing in `@raydium-io/raydium-sdk-v2` is exposed on `raydium.tradeV2`. The end-to-end shape — fetching pool data, computing routes, ranking by output, and building the swap transaction — is shown below; this matches the canonical example in [`raydium-sdk-V2-demo/src/trade/routeSwap.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/trade/routeSwap.ts).

```typescript theme={null}
// 1. Choose input/output mints and amount.
const inputMint = NATIVE_MINT;                                     // wSOL
const outputMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC
const inputAmount = "8000000";                                     // 0.008 SOL (lamports)

// 2. Fetch all pool basic info. Cache this aggressively — it's a large RPC fan-out.
const poolData = await raydium.tradeV2.fetchRoutePoolBasicInfo();

// 3. Enumerate every viable route across AMM v4, CPMM, CLMM, and Stable AMM.
const routes = raydium.tradeV2.getAllRoute({
  inputMint,
  outputMint,
  ...poolData,
});

// 4. Hydrate routes with the live RPC state (reserves, ticks, mint info).
const {
  routePathDict,
  mintInfos,
  ammPoolsRpcInfo,
  ammSimulateCache,
  clmmPoolsRpcInfo,
  computeClmmPoolInfo,
  computePoolTickData,
  computeCpmmData,
} = await raydium.tradeV2.fetchSwapRoutesData({
  routes,
  inputMint,
  outputMint,
});

// 5. Compute output amounts for every candidate route. Result is sorted descending by output.
const swapRoutes = raydium.tradeV2.getAllRouteComputeAmountOut({
  inputTokenAmount: new TokenAmount(
    new Token({
      mint: inputMint.toBase58(),
      decimals: mintInfos[inputMint.toBase58()].decimals,
      isToken2022: mintInfos[inputMint.toBase58()].programId.equals(TOKEN_2022_PROGRAM_ID),
    }),
    inputAmount,
  ),
  directPath: routes.directPath.map(
    (p) =>
      ammSimulateCache[p.id.toBase58()] ||
      computeClmmPoolInfo[p.id.toBase58()] ||
      computeCpmmData[p.id.toBase58()],
  ),
  routePathDict,
  simulateCache: ammSimulateCache,
  tickCache: computePoolTickData,
  mintInfos,
  outputToken: toApiV3Token({
    ...mintInfos[outputMint.toBase58()],
    programId: mintInfos[outputMint.toBase58()].programId.toBase58(),
    address: outputMint.toBase58(),
    freezeAuthority: undefined,
    mintAuthority: undefined,
    extensions: { feeConfig: toFeeConfig(mintInfos[outputMint.toBase58()].feeConfig) },
  }),
  chainTime: Math.floor(raydium.chainTimeData?.chainTime ?? Date.now() / 1000),
  slippage: 0.005, // 0.5%
  epochInfo: await raydium.connection.getEpochInfo(),
});

const targetRoute = swapRoutes[0];
if (!targetRoute) throw new Error("no swap routes were found");

// 6. Resolve the pool keys for the chosen route.
const poolKeys = await raydium.tradeV2.computePoolToPoolKeys({
  pools: targetRoute.poolInfoList,
  ammRpcData: ammPoolsRpcInfo,
  clmmRpcData: clmmPoolsRpcInfo,
});

// 7. Build, sign, and execute the swap transaction(s).
const { execute, transactions } = await raydium.tradeV2.swap({
  routeProgram: Router,
  txVersion: TxVersion.V0,
  swapInfo: targetRoute,
  swapPoolKeys: poolKeys,
  ownerInfo: {
    associatedOnly: true,
    checkCreateATAOwner: true,
  },
  computeBudgetConfig: {
    units: 600_000,
    microLamports: 465_915,
  },
});

// `sequentially: true` is important — the first transaction may need to create
// intermediate ATAs that subsequent transactions rely on.
const { txIds } = await execute({ sequentially: true });
console.log("txIds:", txIds);
```

### Expected behavior

The SDK handles:

* Route discovery across AMM v4, CPMM, CLMM, and Stable AMM.
* Account derivation (pool states, vaults, observation accounts, ATA pre-creation).
* Instruction packing for the router program (`Router`) when the route is multi-hop, or a direct pool swap when a single pool already gives the best price.
* Slippage enforcement via the `slippage` parameter on `getAllRouteComputeAmountOut`.

`raydium.tradeV2.swap` may return more than one `transaction` — the first commonly initializes intermediate ATAs and the second performs the swap itself. Always pass `sequentially: true` to `execute()` so they confirm in order.

***

## Example 2: Raw instruction building (Rust-like pseudocode)

If you need finer control or are building a program that CPIs into the router, construct instructions manually. The example below uses **tag 8 (`SwapBaseIn`)** — the recommended Current variant — and routes through user-owned ATAs end to end.

### Scenario: USDC → SOL (CPMM) → mSOL (CPMM)

#### Step 1: Derive the user's ATAs

```typescript theme={null}
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";

const ROUTER_PROGRAM_ID = new PublicKey(
  "routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS"
);

const USDC_MINT = new PublicKey(
  "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
);
const SOL_MINT = new PublicKey(
  "So11111111111111111111111111111111111111112"
);
const MSOL_MINT = new PublicKey(
  "mSoL1MK4LCEDcTYVhPnD6DHK4PMGQ8WyXDEMXbnQAaW"
);

// User input / intermediate / output ATAs — all owned by the caller.
const user_usdc_ata = getAssociatedTokenAddressSync(USDC_MINT, wallet.publicKey);
const user_sol_ata  = getAssociatedTokenAddressSync(SOL_MINT,  wallet.publicKey, true); // wSOL: allowOwnerOffCurve
const user_msol_ata = getAssociatedTokenAddressSync(MSOL_MINT, wallet.publicKey);

// If the user's intermediate / output ATAs do not yet exist, create them up
// front via the Associated Token Account program (or use CreateSyncNative for
// the wSOL ATA on tag 5).
```

#### Step 2: Gather accounts for each hop

Hop 1 is USDC/SOL on CPMM. Hop 2 is SOL/mSOL on CPMM.

```typescript theme={null}
// Hop 1: USDC/SOL swap on CPMM
const CPMM_PROGRAM_ID = new PublicKey(
  "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"
);

// Fetch pool state for USDC/SOL
const usdc_sol_pool = await connection.getParsedAccountInfo(
  USDC_SOL_POOL_STATE
);

// Pool state, authority, vaults, mints, token programs, etc.
// (see products/cpmm/accounts for the full 11-account list)
const hop1_accounts = [
  CPMM_PROGRAM_ID,           // Identifies this as CPMM
  USDC_SOL_POOL_STATE,
  CPMM_AUTHORITY,
  USDC_SOL_VAULT_TOKEN0,
  USDC_SOL_VAULT_TOKEN1,
  USDC_MINT,
  SOL_MINT,
  LP_MINT,
  OBSERVATION_STATE,
  TOKEN_PROGRAM,
  ASSOCIATED_TOKEN_PROGRAM,
  SYSTEM_PROGRAM,
];

// Hop 2: SOL/mSOL swap on CPMM
const sol_msol_pool = await connection.getParsedAccountInfo(
  SOL_MSOL_POOL_STATE
);

const hop2_accounts = [
  CPMM_PROGRAM_ID,           // Identifies this as CPMM
  SOL_MSOL_POOL_STATE,
  CPMM_AUTHORITY,
  SOL_MSOL_VAULT_TOKEN0,
  SOL_MSOL_VAULT_TOKEN1,
  SOL_MINT,
  MSOL_MINT,
  LP_MINT_2,
  OBSERVATION_STATE_2,
  TOKEN_PROGRAM,
  ASSOCIATED_TOKEN_PROGRAM,
  SYSTEM_PROGRAM,
];
```

#### Step 3: Build the instruction

```typescript theme={null}
import BN from "bn.js";

// Arguments
const amount_in = new BN(1_000_000_000); // 1000 USDC (6 decimals)
const minimum_amount_out = new BN(500_000_000_000); // 500 mSOL (9 decimals)

// No CLMM hops, so limit_prices is empty
const limit_prices: BN[] = [];

// Pack arguments
let data = Buffer.alloc(1024);
let offset = 0;

// Tag 8: SwapBaseIn (Current variant; empty limit_prices is allowed)
data[offset++] = 8;

// amount_in (u64)
data.writeBigUInt64LE(BigInt(amount_in.toString()), offset);
offset += 8;

// minimum_amount_out (u64)
data.writeBigUInt64LE(BigInt(minimum_amount_out.toString()), offset);
offset += 8;

// limit_prices (VecDeque<u128>) — empty (no CLMM hops in this route).
// Tag 8 accepts an empty deque without error. Write zero entries (no u128s).

data = data.slice(0, offset);

// Construct accounts list — every intermediate ATA is user-owned.
const accounts = [
  { pubkey: user_usdc_ata, isSigner: true,  isWritable: true },
  { pubkey: user_sol_ata,  isSigner: false, isWritable: true },
  { pubkey: user_msol_ata, isSigner: false, isWritable: true },
  ...hop1_accounts.map((pk, i) => ({
    pubkey: pk,
    isSigner: false,
    isWritable: i === 0 ? false : true, // program ID is not writable
  })),
  ...hop2_accounts.map((pk, i) => ({
    pubkey: pk,
    isSigner: false,
    isWritable: i === 0 ? false : true,
  })),
];

const instruction = new TransactionInstruction({
  programId: ROUTER_PROGRAM_ID,
  keys: accounts,
  data,
});
```

#### Step 4: Send transaction

```typescript theme={null}
const { blockhash } = await connection.getLatestBlockhash();

const tx = new VersionedTransaction(
  new TransactionMessage({
    instructions: [instruction],
    payerKey: wallet.publicKey,
    recentBlockhash: blockhash,
  }).compileToV0Message()
);

tx.sign([wallet]);
const sig = await connection.sendTransaction(tx);
await connection.confirmTransaction(sig);
```

***

## Example 3: Error handling

Common errors and how to recover:

### ExceededSlippage

The output was less than `minimum_amount_out`. Retry with higher slippage tolerance or re-quote the route.

```typescript theme={null}
try {
  const sig = await connection.sendTransaction(tx);
} catch (error) {
  if (error.message.includes("ExceededSlippage")) {
    console.log(
      "Route price moved. Re-quote and rebuild transaction."
    );
  }
}
```

### SqrtPriceX64 (CLMM)

A CLMM hop's price drifted outside the `limit_prices` bounds. Update the bounds and retry.

### InvalidOwner

An intermediate or output ATA is not owned by the caller. The router validates ownership on every slot; ensure each ATA you pass was derived from the user's wallet (not from any other authority).

***

## Tips and best practices

### Pre-create intermediate ATAs

Before routing through a new intermediate token for the first time, create the user's ATA so the route does not fail validation:

```typescript theme={null}
import { createAssociatedTokenAccountInstruction } from "@solana/spl-token";

const createAtaIx = createAssociatedTokenAccountInstruction(
  wallet.publicKey,  // payer
  user_sol_ata,      // ATA to create
  wallet.publicKey,  // owner — always the user
  SOL_MINT,
);

// Bundle createAtaIx into the same transaction as the first route, or send it
// once up front; the rent (~0.002 SOL) is reclaimable later via tag 6
// (CloseTokenAccount) once the ATA is empty.
```

For wSOL specifically, prefer [`CreateSyncNative`](/products/routing/instructions#createsyncnative-tag-5) (tag 5) — it creates the ATA, transfers the SOL, and syncs in one instruction.

### Quote before executing

Always query the pools and compute expected output before building the instruction:

```typescript theme={null}
// Pseudocode: get the current pool state and compute price
const pool1State = await connection.getAccountInfo(USDC_SOL_POOL_STATE);
const amount_out_hop1 = computeSwapOutput(
  amount_in,
  pool1State,
  USDC_MINT,
  SOL_MINT
);

const pool2State = await connection.getAccountInfo(SOL_MSOL_POOL_STATE);
const amount_out_hop2 = computeSwapOutput(
  amount_out_hop1,
  pool2State,
  SOL_MINT,
  MSOL_MINT
);

const minimum_amount_out = amount_out_hop2 * 0.99; // 1% slippage
```

### Use the newer instruction variants (8–9)

Tags 8 and 9 (`SwapBaseIn` and `SwapBaseOut`) are more forgiving with `limit_prices`. Prefer them over the legacy variants if you don't need CLMM price validation.

```typescript theme={null}
// Tag 8: SwapBaseIn with optional limit_prices
data[offset++] = 8;
// ... rest of packing; limit_prices deque can be empty
```

***

## Where to go next

* [`products/routing/instructions`](/products/routing/instructions) — full instruction reference.
* [`products/routing/accounts`](/products/routing/accounts) — account derivation details.
* [`products/cpmm/code-demos`](/products/cpmm/code-demos) — CPMM swap examples for comparison.
* [`reference/program-addresses`](/reference/program-addresses) — canonical program IDs and devnet addresses.

Sources:

* [`@raydium-io/raydium-sdk-v2` on npm](https://www.npmjs.com/package/@raydium-io/raydium-sdk-v2)
* [`raydium-io/raydium-sdk-V2`](https://github.com/raydium-io/raydium-sdk-V2) (open source)
* [`raydium-io/raydium-sdk-V2-demo`](https://github.com/raydium-io/raydium-sdk-V2-demo) — official end-to-end demos including `routeSwap.ts`
* AMM Routing program source is not publicly available; verify the program ID against the live API and the on-chain bytecode
