A C# SDK & Client for Jupiter's Perpetuals Program on Solana
Solnet.JupiterPerps
C# SDK & Client for interacting with the Jupiter Perpetuals on-chain program on Solana.
Overview
Solnet.JupiterPerps is a .NET 8.0 library that enables developers, trading bots, quant systems, and backend services to read on-chain state and craft valid transactions for Jupiter's Perpetuals Program. It builds on top of the official Solnet stack (Solana.Rpc, Solana.Programs, Solana.Extensions, Solana.KeyStore).
The SDK exposes:
- Low-level instruction builders (via
PerpetualsProgram) for every supported on-chain instruction. - A typed async client (
PerpetualsClient) to fetch and deserialize program accounts (pools, custodies, positions, position requests, etc.). - PDA helpers in
PDALookupto derive program-derived addresses deterministically. - Strongly-typed parameters & account record classes under
Records/andTypes/namespaces.
Key Capabilities (Current)
- Discover and load program state: Perpetuals config, pools, custodies, positions, position requests.
- Create increase & decrease position market requests including SOL collateral wrapping.
- Build raw Solana
TransactionInstructionobjects for advanced / custom pipelines. - Liquidate or close positions (where permitted by program state and rules).
- Derive PDAs for custody, pool, and perpetuals accounts.
Architecture at a Glance
Solnet.JupiterPerps/ ├─ JupiterPerpsProgram.cs // Static instruction builders (serialization + account metas) ├─ JupiterPerpsClient.cs // High-level async RPC client wrappers ├─ Utils.cs // PDA derivation helpers (PDALookup) ├─ Errors.cs // Enum mapping on-chain error codes → names ├─ Records/ // Account meta groupings for each instruction └─ Types/ // Parameter + account layout types (Borsh-like serialization)
Instruction Builders
Each static method onPerpetualsProgram returns a fully populated TransactionInstruction:
var ix = PerpetualsProgram.IncreasePosition4(accounts, paramsObj);
These builders:
- Set the correct discriminator / instruction tag (first 8 bytes).
- Serialize parameters into the byte buffer.
- Attach ordered account metas (read-only / writable / signer flags) as required by the on-chain program.
High-Level Client
PerpetualsClient wraps RPC account fetch flows with discriminator-based filtering using getProgramAccounts and typed deserialization:
var rpc = ClientFactory.GetClient(Cluster.MainNet); // from Solnet
var perpClient = new PerpetualsClient(rpc);
var pools = await perpClient.GetPoolsAsync();
PDA Utilities
Deterministic derivations to avoid manual seed handling:PublicKey custody = PDALookup.FindCustodyPDA(tokenMint);
PublicKey pool = PDALookup.FindPoolPDA(ownerOrSeedKey);
PublicKey perpCfg = PDALookup.FindPerpetualsPDA(adminOrSeedKey);
Installation
Add the project as a dependency (until published to NuGet):- Clone the repository.
- Reference the
.csprojfrom your solution or add a project reference:
<ItemGroup>
<ProjectReference Include="..\Solnet.JupiterPerps\Solnet.JupiterPerps.csproj" />
</ItemGroup>
- Restore & build:
dotnet restore; dotnet build -c Release
Planned: Publish to NuGet as Solnet.JupiterPerps once APIs stabilize.
Quick Start Example: Open / Increase a Position
using Solnet.JupiterPerps;
using Solnet.Wallet;
using Solnet.Rpc;
using Solnet.Rpc.Types;
// 1. Initialize RPC + wallet var rpc = ClientFactory.GetClient(Cluster.MainNet); var trader = new Wallet.Wallet(new Mnemonic("<seed words>")); var perpClient = new PerpetualsClient(rpc);
// 2. Build accounts + params (illustrative – adapt to real account keys) var accounts = new CreateIncreasePositionMarketRequestAccounts { // Populate: user account, pool, custody, position request PDA, token accounts, oracle feeds, etc. }; var parameters = new CreateIncreasePositionMarketRequestParams { // Populate: size, leverage, side, slippage tolerances, etc. };
// 3. Fund with SOL collateral (lamports) and submit combined transaction ulong collateralLamports = 1000000_000; // 1 SOL example string rawResponse = await perpClient.SendCreateIncreasePositionAsync(trader.Account, collateralLamports, accounts, parameters); Console.WriteLine(rawResponse);
Building Custom Transactions
If you need to batch multiple Jupiter Perps instructions, you can manually chain builders:var ix1 = PerpetualsProgram.CreateIncreasePositionMarketRequest(accts1, params1);
var ix2 = PerpetualsProgram.InstantCreateLimitOrder(accts2, limitParams);
var blockhash = (await rpc.GetLatestBlockHashAsync()).Result.Value.Blockhash; var tx = new TransactionBuilder() .SetRecentBlockHash(blockhash) .SetFeePayer(traderPubKey) .AddInstruction(ComputeBudgetProgram.SetComputeUnitLimit(120_000)) .AddInstruction(ix1) .AddInstruction(ix2) .Build(traderAccount);
var sig = await rpc.SendTransactionAsync(tx);
Error Handling
On-chain custom errors are enumerated inPerpetualsErrorKind (see Errors.cs). A non-exhaustive subset:
| Code | Name | Meaning |
|------|------|---------|
| 6000 | MathOverflow | Internal arithmetic overflow detected |
| 6022 | MaxLeverage | Requested leverage exceeds pool or custody limits |
| 6021 | MaxPriceSlippage | Slippage tolerance breached |
| 6019 | InsufficientTokenAmount | Not enough tokens provided for the action |
| 6063 | CannotLiquidate | Liquidation conditions not met |
When a transaction fails, decode the InstructionError → custom error code and map to the enum for user-friendly messaging.
Security & Reliability Notes
- Always verify oracle freshness (stale price errors 6003 / 6046 appear if not).
- Wrap SOL correctly (the helper flow in
SendCreateIncreasePositionAsynccreates and syncs the associated wSOL ATA). - Monitor compute unit usage; complex position flows may require budget increases (
ComputeBudgetProgram). - Validate PDAs locally rather than trusting external input.
Roadmap / Planned Enhancements
- Automatic position close helper (removing current manual requirement).
- Take Profit / Stop Loss management & update flows (TPSL convenience wrappers).
- Signed backend co-signature integration (Jupiter API) for close path optimizations.
- NuGet packaging & semantic versioning.
- Expanded high-level convenience methods (e.g., margin health checks, liquidation candidates scan).
- Optional caching layer for frequently polled accounts.
Contributing
Pull requests welcome. Please ensure:- Deterministic instruction ordering.
- No breaking changes to public method signatures without discussion.
- Clear commit messages referencing instruction / feature touched.
Development
git clone https://github.com/Bifrost-Technologies/Solnet.JupiterPerps.git
cd Solnet.JupiterPerps/Solnet.JupiterPerps
dotnet build
Run a quick smoke test by attempting to fetch pools:
var rpc = ClientFactory.GetClient(Cluster.MainNet); var client = new PerpetualsClient(rpc); var pools = await client.GetPoolsAsync(); Console.WriteLine($"Pools fetched: {pools.ParsedResult?.Count}");
Disclaimer
This SDK is an independent, community-maintained integration layer. Use at your own risk. Always test with small amounts and review on-chain program updates.License
MIT – seeLICENSE.
Questions or feature requests? Open an issue or discussion on the repository.