<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vincent Vego</title>
    <description>The latest articles on DEV Community by Vincent Vego (@vincent_vego_db96a8722ed2).</description>
    <link>https://dev.to/vincent_vego_db96a8722ed2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4009351%2Fa1a50bb1-e3e7-46f2-a45f-dbe4827686cb.png</url>
      <title>DEV Community: Vincent Vego</title>
      <link>https://dev.to/vincent_vego_db96a8722ed2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vincent_vego_db96a8722ed2"/>
    <language>en</language>
    <item>
      <title>Building a Token Swap on Solana in 50 Lines of TypeScript (Using DFlow's JIT Routing)</title>
      <dc:creator>Vincent Vego</dc:creator>
      <pubDate>Tue, 30 Jun 2026 09:49:03 +0000</pubDate>
      <link>https://dev.to/vincent_vego_db96a8722ed2/building-a-token-swap-on-solana-in-50-lines-of-typescript-using-dflows-jit-routing-4768</link>
      <guid>https://dev.to/vincent_vego_db96a8722ed2/building-a-token-swap-on-solana-in-50-lines-of-typescript-using-dflows-jit-routing-4768</guid>
      <description>&lt;p&gt;I spent last week integrating every major swap API on Solana into a side project. Jupiter, Raydium directly, a couple of smaller ones. Then I tried DFlow — and it's the one I kept.&lt;br&gt;
Not because of branding or docs (though the docs are solid). Because of one technical decision that changes everything: JIT routing.&lt;br&gt;
This post walks through what JIT routing actually is, why it produces better results than pre-computed routing, and how to build a working token swap in ~50 lines of TypeScript.&lt;/p&gt;

&lt;p&gt;The Problem With Pre-Computed Routes&lt;br&gt;
Every swap aggregator on Solana works roughly the same way:&lt;/p&gt;

&lt;p&gt;You request a quote → the API computes the best route across liquidity venues&lt;br&gt;
You receive a serialized transaction with that route baked in&lt;br&gt;
You sign and submit&lt;br&gt;
The transaction lands on-chain 400ms–2s later&lt;/p&gt;

&lt;p&gt;The issue is step 4. Between when the route was computed and when your transaction executes, the market has moved. Pools rebalance, other trades land, arbitrage bots reposition. The route that was optimal at quote time is stale at execution time.&lt;br&gt;
This is why the price you see is never the price you get. The gap is small on each trade but compounds fast.&lt;br&gt;
JIT (Just-In-Time) routing solves this by deferring route optimization to the moment of on-chain execution — not quote time. The route is computed and executed atomically. No stale data. No gap.&lt;br&gt;
DFlow's entire Trading API is built around this. Every swap, every trade, every fill goes through the JIT engine.&lt;/p&gt;

&lt;p&gt;What We're Building&lt;br&gt;
A simple TypeScript script that:&lt;/p&gt;

&lt;p&gt;Connects to a Solana wallet&lt;br&gt;
Swaps SOL → USDC through DFlow's Swap API&lt;br&gt;
Uses declarative mode (intent-based, protocol handles routing)&lt;br&gt;
Streams real-time market data via WebSocket&lt;/p&gt;

&lt;p&gt;If you want the full picture of DFlow's products before diving into code, dflow-trade.com has a good overview — Swap API, JIT Routing, Prediction Markets, and the new MCP tooling for AI agents.&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;br&gt;
bashnpm init -y&lt;br&gt;
npm install @solana/web3.js@1 bs58 dotenv&lt;br&gt;
Create a .env file:&lt;br&gt;
envDFLOW_API_KEY=your_api_key_here&lt;br&gt;
RPC_URL=&lt;a href="https://api.mainnet-beta.solana.com" rel="noopener noreferrer"&gt;https://api.mainnet-beta.solana.com&lt;/a&gt;&lt;br&gt;
WALLET_PRIVATE_KEY=your_base58_private_key&lt;/p&gt;

&lt;p&gt;API key: contact &lt;a href="mailto:api@dflow.net"&gt;api@dflow.net&lt;/a&gt; or check DFlow docs for access.&lt;/p&gt;

&lt;p&gt;Step 1: The Swap Function&lt;br&gt;
Here's the core swap — request a quote, deserialize the transaction, sign, send:&lt;br&gt;
typescript// swap.ts&lt;br&gt;
import { Connection, Keypair, VersionedTransaction } from "@solana/web3.js";&lt;br&gt;
import bs58 from "bs58";&lt;br&gt;
import "dotenv/config";&lt;/p&gt;

&lt;p&gt;// Config&lt;br&gt;
const API_BASE = "&lt;a href="https://quote-api.dflow.net" rel="noopener noreferrer"&gt;https://quote-api.dflow.net&lt;/a&gt;";&lt;br&gt;
const API_KEY = process.env.DFLOW_API_KEY!;&lt;br&gt;
const connection = new Connection(process.env.RPC_URL!);&lt;br&gt;
const wallet = Keypair.fromSecretKey(bs58.decode(process.env.WALLET_PRIVATE_KEY!));&lt;/p&gt;

&lt;p&gt;// Token mints&lt;br&gt;
const SOL = "So11111111111111111111111111111111111111112";&lt;br&gt;
const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";&lt;/p&gt;

&lt;p&gt;async function swap(&lt;br&gt;
  inputMint: string,&lt;br&gt;
  outputMint: string,&lt;br&gt;
  amountLamports: string,&lt;br&gt;
  slippageBps: string = "50"&lt;br&gt;
) {&lt;br&gt;
  // 1. Request optimized quote&lt;br&gt;
  const params = new URLSearchParams({&lt;br&gt;
    inputMint,&lt;br&gt;
    outputMint,&lt;br&gt;
    amount: amountLamports,&lt;br&gt;
    slippageBps,&lt;br&gt;
    userPublicKey: wallet.publicKey.toBase58(),&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;const response = await fetch(&lt;code&gt;${API_BASE}/order?${params}&lt;/code&gt;, {&lt;br&gt;
    headers: { "x-api-key": API_KEY },&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;if (!response.ok) {&lt;br&gt;
    throw new Error(&lt;code&gt;Quote failed: ${response.status} ${await response.text()}&lt;/code&gt;);&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;const order = await response.json();&lt;/p&gt;

&lt;p&gt;// 2. Deserialize and sign&lt;br&gt;
  const tx = VersionedTransaction.deserialize(&lt;br&gt;
    Buffer.from(order.transaction, "base64")&lt;br&gt;
  );&lt;br&gt;
  tx.sign([wallet]);&lt;/p&gt;

&lt;p&gt;// 3. Send with preflight checks&lt;br&gt;
  const signature = await connection.sendRawTransaction(tx.serialize(), {&lt;br&gt;
    skipPreflight: false,&lt;br&gt;
    maxRetries: 3,&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;// 4. Confirm&lt;br&gt;
  const confirmation = await connection.confirmTransaction(signature, "confirmed");&lt;/p&gt;

&lt;p&gt;if (confirmation.value.err) {&lt;br&gt;
    throw new Error(&lt;code&gt;Transaction failed: ${JSON.stringify(confirmation.value.err)}&lt;/code&gt;);&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;return {&lt;br&gt;
    signature,&lt;br&gt;
    inputAmount: amountLamports,&lt;br&gt;
    expectedOutput: order.expectedOutput,&lt;br&gt;
    priceImpact: order.priceImpact,&lt;br&gt;
  };&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// Execute: swap 0.1 SOL → USDC&lt;br&gt;
swap(SOL, USDC, "100000000")&lt;br&gt;
  .then((result) =&amp;gt; {&lt;br&gt;
    console.log("Swap successful!");&lt;br&gt;
    console.log(&lt;code&gt;Signature: ${result.signature}&lt;/code&gt;);&lt;br&gt;
    console.log(&lt;code&gt;Expected output: ${result.expectedOutput}&lt;/code&gt;);&lt;br&gt;
    console.log(&lt;code&gt;Price impact: ${result.priceImpact}%&lt;/code&gt;);&lt;br&gt;
  })&lt;br&gt;
  .catch(console.error);&lt;br&gt;
Run it:&lt;br&gt;
bashnpx tsx swap.ts&lt;br&gt;
That's it. ~50 lines from zero to executed swap. The JIT routing, MEV protection, and multi-venue aggregation all happen inside the API — your code just sends intent and receives a result.&lt;/p&gt;

&lt;p&gt;Step 2: Real-Time Market Data via WebSocket&lt;br&gt;
DFlow provides a WebSocket endpoint for streaming live market updates. This is useful for building trading UIs, monitoring bots, or triggering conditional swaps.&lt;br&gt;
typescript// stream.ts&lt;/p&gt;

&lt;p&gt;const DFLOW_WS = "wss://api.prod.dflow.net/ws";&lt;/p&gt;

&lt;p&gt;function startStream(ticker: string) {&lt;br&gt;
  const ws = new WebSocket(DFLOW_WS);&lt;/p&gt;

&lt;p&gt;ws.onopen = () =&amp;gt; {&lt;br&gt;
    console.log(&lt;code&gt;Connected. Subscribing to ${ticker}...&lt;/code&gt;);&lt;br&gt;
    ws.send(JSON.stringify({&lt;br&gt;
      action: "subscribe",&lt;br&gt;
      channel: "market",&lt;br&gt;
      ticker,&lt;br&gt;
    }));&lt;br&gt;
  };&lt;/p&gt;

&lt;p&gt;ws.onmessage = (event) =&amp;gt; {&lt;br&gt;
    const data = JSON.parse(event.data.toString());&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (data.type) {
  case "price_update":
    console.log(`[${ticker}] Price: ${data.price} | Volume: ${data.volume}`);
    break;
  case "orderbook_update":
    console.log(`[${ticker}] Book depth: ${data.bids?.length} bids, ${data.asks?.length} asks`);
    break;
  case "trade":
    console.log(`[${ticker}] Trade: ${data.side} ${data.amount} @ ${data.price}`);
    break;
  default:
    console.log(`[${ticker}] ${data.type}:`, data);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;};&lt;/p&gt;

&lt;p&gt;ws.onerror = (err) =&amp;gt; console.error("WebSocket error:", err);&lt;/p&gt;

&lt;p&gt;ws.onclose = () =&amp;gt; {&lt;br&gt;
    console.log("Disconnected. Reconnecting in 3s...");&lt;br&gt;
    setTimeout(() =&amp;gt; startStream(ticker), 3000);&lt;br&gt;
  };&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;startStream("SOL-USDC");&lt;br&gt;
Auto-reconnect included. In production you'd add exponential backoff and a message queue, but this covers the basics.&lt;/p&gt;

&lt;p&gt;Step 3: Declarative vs Imperative — When to Use Which&lt;br&gt;
DFlow supports two swap modes. Understanding the difference matters for your architecture.&lt;br&gt;
Imperative Swaps&lt;br&gt;
You request quote → API returns a specific route → You sign that exact route&lt;br&gt;
Use when: your UI needs to show the user exactly which venues their trade goes through. Think "transparency mode" — the user sees "50% through Venue A, 50% through Venue B" before signing.&lt;br&gt;
Trade-off: the route is frozen at signature time. If conditions change between signing and landing, you get a worse fill or a failed tx.&lt;br&gt;
Declarative Swaps&lt;br&gt;
You submit intent ("swap X for Y, min output Z") → Protocol optimizes at execution&lt;br&gt;
Use when: you want the best possible execution and don't need to display route details. This is what trading bots, backend services, and high-frequency apps should use.&lt;br&gt;
Advantage: route adapts to real-time conditions at the moment of finality. Fewer failed transactions. Better fills. And because no pre-computed route is broadcast, sandwich bots can't front-run your trade — that's MEV protection built into the architecture.&lt;br&gt;
For most applications, declarative is the better default. Use imperative only when route transparency is a product requirement.&lt;/p&gt;

&lt;p&gt;Bonus: Prediction Markets in 20 Lines&lt;br&gt;
DFlow also tokenizes Kalshi prediction markets on Solana. Here's how you'd fetch available markets and check prices:&lt;br&gt;
typescript// predictions.ts&lt;br&gt;
const PRED_API = "&lt;a href="https://prediction-markets-api.dflow.net" rel="noopener noreferrer"&gt;https://prediction-markets-api.dflow.net&lt;/a&gt;";&lt;/p&gt;

&lt;p&gt;async function getMarkets(category: string = "sports") {&lt;br&gt;
  const response = await fetch(&lt;code&gt;${PRED_API}/markets?category=${category}&lt;/code&gt;, {&lt;br&gt;
    headers: { "x-api-key": process.env.DFLOW_API_KEY! },&lt;br&gt;
  });&lt;br&gt;
  return response.json();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;async function getPrice(marketId: string) {&lt;br&gt;
  const response = await fetch(&lt;code&gt;${PRED_API}/markets/${marketId}/price&lt;/code&gt;, {&lt;br&gt;
    headers: { "x-api-key": process.env.DFLOW_API_KEY! },&lt;br&gt;
  });&lt;br&gt;
  return response.json();&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// List sports markets and check first one&lt;br&gt;
getMarkets("sports").then(async (markets) =&amp;gt; {&lt;br&gt;
  console.log(&lt;code&gt;Found ${markets.length} markets\n&lt;/code&gt;);&lt;/p&gt;

&lt;p&gt;for (const m of markets.slice(0, 5)) {&lt;br&gt;
    const price = await getPrice(m.id);&lt;br&gt;
    console.log(&lt;code&gt;${m.title}&lt;/code&gt;);&lt;br&gt;
    console.log(&lt;code&gt;YES: $${price.yes} | NO: $${price.no}&lt;/code&gt;);&lt;br&gt;
    console.log(&lt;code&gt;Closes: ${m.closeTime}\n&lt;/code&gt;);&lt;br&gt;
  }&lt;br&gt;
});&lt;br&gt;
Each position is a real SPL token. You can hold it in Phantom, transfer it, or build composable strategies on top. When the event resolves, winning tokens are redeemed for USDC.&lt;br&gt;
Kalshi is CFTC-regulated, so these aren't offshore prediction bets — they're federally regulated event contracts tokenized on Solana. Kalshi backed the ecosystem with a $2M grants program for builders.&lt;/p&gt;

&lt;p&gt;Architecture Recap&lt;br&gt;
Here's how everything connects:&lt;br&gt;
Your App&lt;br&gt;
  │&lt;br&gt;
  ├─→ Swap API ──→ JIT Routing Engine ──→ Liquidity Venues&lt;br&gt;
  │                    (real-time optimization + MEV protection)&lt;br&gt;
  │&lt;br&gt;
  ├─→ Prediction Markets API ──→ CLPs ──→ Kalshi (off-chain)&lt;br&gt;
  │                                        (CFTC-regulated)&lt;br&gt;
  │&lt;br&gt;
  └─→ WebSocket Stream ──→ Live market data&lt;br&gt;
One API. Three product surfaces. All non-custodial, all on Solana.&lt;/p&gt;

&lt;p&gt;Resources&lt;br&gt;
WhatWhereProduct overview + FAQdflow-trade.comAPI docspond.dflow.netSecurity auditsdflow-trade.com/#securityTwitter@DFlowProtocolAPI key &lt;a href="mailto:requestapi@dflow.net"&gt;requestapi@dflow.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Takeaways&lt;br&gt;
If you're building anything that involves token swaps on Solana, the execution layer you choose matters more than most people realize. The difference between pre-computed routing and JIT routing is the difference between "close enough" and "actually optimal."&lt;br&gt;
DFlow handles routing, MEV protection, and settlement — you handle the product. That's a good split.&lt;br&gt;
Full product breakdown, security audits, and a 30-question FAQ at dflow-trade.com.&lt;/p&gt;

&lt;p&gt;Have questions or building something on DFlow? Drop a comment — happy to dig into the technical details.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>100daysofsolana</category>
      <category>cryptocurrency</category>
    </item>
  </channel>
</rss>
