C# Client Wrapper for Jupiter Aggregator v6 Swap API (Solana)
Solnet.JupiterSwap is a lightweight, strongly-typed C# wrapper around Jupiter's v6 Swap Aggregator API. It streamlines:
- Fetching optimal swap routes (quotes)
- Building unsigned swap transactions (legacy by default)
- Token metadata retrieval (strict vs full list)
- Simple interop with existing
Solnetwallet, RPC and program utilities
Status: Legacy (non-versioned) transaction flow is implemented and tested. Versioned (v0) transactions require additional work (see Roadmap).
- Quote retrieval with configurable slippage & routing constraints
- Swap transaction generation (unsigned) with support for:
- Shared accounts toggle
- Auto wrap/unwrap SOL
- Legacy transaction selection
- Token list hydration (strict / all)
- High-level strongly typed models:
SwapQuoteAg,SwapRequest,SwapResponse,TokenData - Simple extension entry point via
JupiterDexAg : IDexAggregator
Package not yet published to NuGet (if you need this, open an issue). For now:
- Clone the repository
- Add the
Solnet.JupiterSwapproject to your solution - Reference it from your application project
Prerequisites:
- .NET 8 SDK
- A Solana RPC endpoint (HTTPS) with sufficient rate limits
- A funded keypair for signing & paying network fees
Below is a minimal end‑to‑end example performing a USDC -> SOL style swap (adjust mints + amount as needed).
using Solnet.JupiterSwap;
using Solnet.JupiterSwap.Models;
using Solnet.JupiterSwap.Types;
using Solnet.Rpc;
using Solnet.Rpc.Models;
using Solnet.Programs;
using Solnet.Wallet;
using System.Numerics;
// 1. RPC + wallet
IRpcClient rpc = ClientFactory.GetClient("https://your-rpc-endpoint");
Account trader = Account.FromSecretKey("<BASE58_SECRET_KEY>");
// 2. Aggregator client (optionally pass PublicKey)
var jupiter = new JupiterDexAg(trader.PublicKey);
// 3. Define mints (USDC -> SOL example; replace if needed)
PublicKey inputMint = new("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC
PublicKey outputMint = new("So11111111111111111111111111111111111111112"); // SOL (wrapped)
// 4. Amounts are in base units (lamports)
// USDC has 6 decimals -> 1.5 USDC = 1_500_000
BigInteger amountIn = new BigInteger(1_500_000);
// 5. Get quote (ExactIn with 0.50% slippage)
SwapQuoteAg quote = await jupiter.GetSwapQuote(
inputMint,
outputMint,
amountIn,
swapMode: SwapMode.ExactIn,
slippageBps: 50 // 50 bps = 0.50%
);
// 6. Build unsigned legacy transaction
Transaction unsignedSwap = await jupiter.Swap(
quote,
userPublicKey: trader.PublicKey,
useSharedAccounts: false,
wrapAndUnwrapSol: true,
asLegacy: true
);
// 7. (OPTIONAL) Rebuild message & sign (pattern defensive vs direct Build)
Message? compiled = Message.Deserialize(unsignedSwap.CompileMessage());
Transaction finalTx = Transaction.Populate(compiled);
byte[] signed = finalTx.Build(trader);
// 8. Send
var sendResult = await rpc.SendTransactionAsync(signed);
Console.WriteLine(sendResult.RawRpcResponse);- SOL: 1 SOL = 1_000_000_000 lamports (9 decimals)
- USDC: 1 USDC = 1_000_000 (6 decimals)
Constructor overloads:
JupiterDexAg(string endpoint = "https://quote-api.jup.ag/v6")JupiterDexAg(PublicKey account, string endpoint = ...)
Primary methods (via IDexAggregator):
| Method | Purpose |
|---|---|
GetSwapQuote(...) |
Retrieve best route & amounts |
Swap(...) |
Construct unsigned swap transaction |
GetTokens(tokenListType) |
Fetch token metadata list |
GetTokenBySymbol(symbol) |
Lookup token by symbol |
GetTokenByMint(mint) |
Lookup token by mint |
inputMint/outputMint(PublicKey)amount(BigInteger) – Interpretation depends onswapMode:ExactIn: amount is input token quantityExactOut: amount is desired output token quantity
swapMode:ExactIn | ExactOutslippageBps: optional (basis points)excludeDexes: e.g.["Saber","Aldrin"]onlyDirectRoutes: restrict to single hopplatformFeeBps: optional fee charged (output token for ExactIn, input for ExactOut)maxAccounts: rough upper bound for account planning
quoteResponse: requiredSwapQuoteAgfrom previous stepuserPublicKey: wallet authority (falls back to constructor account)destinationTokenAccount: optional explicit output ATAwrapAndUnwrapSol: auto create/close temporary WSOL ATAuseSharedAccounts: use aggregator shared program accountsasLegacy: request legacy transaction (default true in this wrapper)
Not yet exposed here: dynamic compute unit tuning, token ledger, referral fee account injection (see
SwapRequestmodel fields). They can be added with minor extension work (see Contributing).
Key fields:
InAmount,OutAmount(string raw) + parsedInputAmount,OutputAmountInputMint,OutputMintSlippageBpsRoutePlan(list of route legs with AMM metadata)PriceImpactPct,ContextSlot,TimeTaken
Per-hop AMM route data: amounts, fee mint, label + AMM key.
Contains flags for advanced behaviors (ledger, referral, compute unit strategies, blockhash expiry tuning).
Returns base64 serialized transaction (SwapTransaction) ready for deserialization & signing.
Includes Name, Mint, Symbol, Decimals, LogoURI, optional Coingecko ID, flags: Whitelisted, PoolToken.
GetTokens(TokenListType.Strict) fetches the curated list.
TokenListType.All returns a superset including more experimental or newly added assets.
Caching: First call caches token list in-memory for the lifetime of the JupiterDexAg instance.
Network / API failures throw HttpRequestException with status code context.
Potential sources:
- Invalid mint addresses
- Route not found (upstream returns non-success)
- RPC later rejects swap (slippage exceeded, blockhash expired, account constraints)
Recommended patterns:
try
{
var quote = await jupiter.GetSwapQuote(inputMint, outputMint, amountIn, slippageBps: 30);
}
catch (HttpRequestException ex)
{
// log + classify (ex.StatusCode isn't directly on exception pre .NET 5, inspect Message)
}Jupiter v6 can return versioned (v0) transactions. Current wrapper forces legacy by sending asLegacyTransaction=true both on quote and swap. To add versioned support:
- Expose a public
asLegacyboolean onGetSwapQuote(currently hardcoded to true) - When
false, passasLegacyTransaction=falseto both endpoints - Deserialize returned transaction and sign using Solnet's versioned transaction pathway
- Adjust docs + tests
- Versioned transaction support
- Referral fee account wiring (
feeAccount+platformFeeBpssynergy) - Dynamic compute unit limit & pricing controls
- Optional token ledger workflow
- Test suite (integration harness against a local validator or devnet)
- NuGet package publishing & semantic versioning
PRs welcome. Please:
- Open an issue describing the change
- Keep changes focused (one feature / fix per PR)
- Include summary in the PR description
Suggested future improvements: typed errors, resilience policies (retry/backoff), structured logging hooks, metrics instrumentation, cancellation token support.
- Always validate mints & amounts before relaying user intent
- Consider quoting and immediately executing to reduce route drift
- Monitor slippage tolerance values — 500 bps (5%) is usually too high for stable pairs
Distributed under the MIT License. See LICENSE for details.
Use at your own risk. On-chain interactions involve financial risk; verify outputs and test on devnet when possible.
Open an issue for bugs, feature requests, or questions. If this project helps you ship, consider a star.
Happy building on Solana with Jupiter + C# 🚀