SolSharp 0.6.0
Prefix ReservedSee the version list below for details.
dotnet add package SolSharp --version 0.6.0
NuGet\Install-Package SolSharp -Version 0.6.0
<PackageReference Include="SolSharp" Version="0.6.0" />
<PackageVersion Include="SolSharp" Version="0.6.0" />
<PackageReference Include="SolSharp" />
paket add SolSharp --version 0.6.0
#r "nuget: SolSharp, 0.6.0"
#:package SolSharp@0.6.0
#addin nuget:?package=SolSharp&version=0.6.0
#tool nuget:?package=SolSharp&version=0.6.0
<p align="center"> <img src="https://raw.githubusercontent.com/jecacs/SolSharp/main/assets/logo.png" alt="SolSharp" width="180" /> </p>
SolSharp
A lean, modern .NET SDK for Solana — RPC, WebSocket streaming, and wire-level transaction signing and building.
SolSharp is built for low latency and a small dependency footprint. It is a focused, hackable alternative to the heavier general-purpose SDKs: you get direct control over the wire format and the signing path, without dragging in a large dependency graph. If you are writing bots, indexers, or backend services that talk to Solana from .NET and care about speed and control, this is aimed at you.
Status: 0.6.0 — stable release. SolSharp ships as a single NuGet package —
SolSharp— bundling the Core (primitives + encodings), Wallet (Ed25519 keys, signing, verification, BIP-39/SLIP-0010 mnemonic import), Rpc (HTTP reads + send/simulate + WebSocket streaming + DI), and Programs (instructions + transaction building + signing, durable nonces) assemblies. Versioning follows semver; while on 0.x, minor releases may still carry breaking changes.
📖 New here? Read the usage guide — a task-oriented cookbook covering keys, reads, SPL token state, building/signing/sending transactions, v0 + address lookup tables, decoding transactions, WebSocket subscriptions, confirmation, and more.
Motivation
When this was started, the .NET options for Solana were either unmaintained and stale or heavy and not built for performance — there was no modern, fast, actively-developed client. SolSharp is a from-scratch answer to that: current C#, allocation-conscious, and tuned for latency-sensitive workloads.
Why
- Lean. No kitchen-sink dependency graph.
Coredepends on a single package (base58). - Wire-level control. Hand-rolled, spec-accurate transaction and message encoding — the part most SDKs hide — with Ed25519 signing on a vetted crypto library, all tested against known vectors.
- Latency-minded. Value types, allocation-free hot paths, span-based APIs.
- Modern .NET. C# latest, nullable reference types, code style enforced on build.
Package
SolSharp ships as a single NuGet package — SolSharp — so one dotnet add package SolSharp pulls in
everything. Internally it stays four layered assemblies, bundled into that one package (namespaces are
unchanged: SolSharp.Core.*, SolSharp.Rpc, SolSharp.Wallet, SolSharp.Programs):
Install from NuGet:
dotnet add package SolSharp
<PackageReference Include="SolSharp" Version="0.6.0" />
| Assembly | Purpose | Status |
|---|---|---|
SolSharp.Core |
Primitives, encoding, JSON, program/sysvar constants | Usable |
SolSharp.Wallet |
Ed25519 keys, key parsing, signing and verification | Usable |
SolSharp.Rpc |
HTTP JSON-RPC reads + WebSocket streaming + DI | Usable |
SolSharp.Programs |
Instructions (System/Token/ATA/Memo/Compute Budget/ALT) + transaction building | Usable |
Keeping the split in the source means the layering stays compiler-enforced — dependencies point downward
only: Rpc and Wallet build on Core, and Programs builds on Core and Wallet. Core depends on
nothing else in the solution and pulls no network or crypto package.
See the changelog for what changed in each release.
What's here today
SolSharp.Core:
PublicKey— a 32-byte value type with value equality, base58 parsing, and JSON support.Base58,ShortVec(compact-u16), andBorshReader/BorshWriter— the encodings Solana uses on the wire, plus a bounds-checked reader and writer for Anchor / Borsh account data and instruction arguments.Commitment— an RPC enum that serializes to its exact wire string.SolanaProgramIds,Sysvars,Mints— well-known on-chain addresses, guarded by a test that every constant decodes to a valid 32-byte key.SolanaUnits— SOL ↔ lamports conversion.
using SolSharp.Core.Primitives;
var mint = PublicKey.Parse("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
byte[] raw = mint.ToBytes(); // 32 bytes, allocation-free storage
bool ok = PublicKey.TryParse(input, out var key);
SolSharp.Rpc:
- HTTP JSON-RPC reads — accounts (
getAccountInfo,getMultipleAccounts,getProgramAccountswith memcmp / data-size filters and data slices,getTokenAccountsByOwner,getTokenLargestAccounts,getTokenAccountBalance,getAddressLookupTablefetch + decode), transactions and blocks (getTransaction,getSignaturesForAddress,getSignatureStatuses,getBlock,getBlockHeight,getTransactionCount,getFeeForMessage), and cluster state (getBalance,getSlot,getLatestBlockhash,isBlockhashValid,getEpochInfo,getVersion,getHealth,getSupply,getSlotLeaders,getRecentPrioritizationFees,getTokenSupply,getMinimumBalanceForRentExemption,getVoteAccounts,getInflationReward,getLeaderSchedule,getBlocks,getClusterNodes,requestAirdrop); each typed, fully documented, and tested. - Account-state decoders —
MintandTokenAccount(SPL Token state, viaGetMintAsync/GetTokenAccountAsync),NonceAccount(viaGetNonceAccountAsync),AddressLookupTable, and the Token-2022 extension section (TokenExtensionSet— TLV walking plus typed views for transfer fees, metadata pointer / in-mint metadata, permanent delegate, and more); for other programs, pairgetAccountInfowith Core'sBorshReader. getTransactionreturns the decoded transaction bytes (feed toTransaction.Deserialize) alongside rich metadata — pre/post SOL and token balances, inner (CPI) instructions, loaded lookup-table addresses, logs, and compute units. Failures decode to a typedTransactionError(exposing the program'sCustomcode) onTransactionMeta,SignatureStatus, andSimulateTransactionResult.GetParsedTransactionAsync/GetParsedBlockAsync/GetParsedAccountInfoAsyncreturn the node'sjsonParseddecoding — typed instructions, token balances, account state, and logs without local Borsh work — each instruction keeping both its parsed form and its raw program id / accounts / data.- WebSocket streaming multiplexed over one connection:
SubscribeSlotsAsyncandSubscribeRootsAsync(IAsyncEnumerable),SubscribeLogsAsync,SubscribeAccountAsync,SubscribeParsedAccountAsync,SubscribeProgramAsync,SubscribeSignatureAsync,SubscribeBlocksAsync, andSubscribeParsedBlocksAsync(ChannelReader), with automatic reconnect and resubscribe across dropped connections. - DI registration with a built-in resilience pipeline (retry on transient errors and HTTP 429), plus
AddSolanaWsfor a container-managed streaming client. - JSON-RPC batching —
CreateBatch()queues reads (and sends) and submits them in one HTTP round-trip. SendTransactionAsync/SimulateTransactionAsync— submit a signed transaction or dry-run it for logs and compute units;SendAndConfirmTransactionAsyncsends and waits for confirmation (throwing if the transaction lands but errors). Confirm by polling (GetSignatureStatusesAsync/ConfirmTransactionAsync) or over the WebSocket (SolanaWsClient.ConfirmSignatureAsync).
using SolSharp.Rpc;
// typed client with retries; tune the pipeline via the optional callback
services.AddSolanaRpc("https://your-rpc-endpoint");
// injected SolanaRpcClient
var lamports = await rpc.GetBalanceAsync(account);
// streaming
await using var ws = new SolanaWsClient();
await ws.ConnectAsync(new Uri("wss://your-rpc-endpoint"));
await foreach (var slot in ws.SubscribeSlotsAsync())
Console.WriteLine(slot.Slot);
SolSharp.Wallet:
Keypair— generate a key, or load one withParse(auto-detecting a base58 export, asolana-keygenJSON array, hex, or base64); signs messages and zeroes its secret on dispose (or finalization).- Mnemonic import —
FromMnemonic(thesolana-keygenscheme) andFromMnemonicAtPath(the Phantom / Solflare SLIP-0010 scheme), built on the publicBip39andSlip10helpers and validated against the official test vectors. ISigner— the signing abstraction the transaction builder depends on, so the key stays swappable.PublicKey.Verify(message, signature)— Ed25519 verification, kept in Wallet so Core stays crypto-free.
using SolSharp.Wallet;
using var keypair = Keypair.Generate(); // or Keypair.Parse(phantomExport / id.json)
using var wallet = Keypair.FromMnemonicAtPath(words, "m/44'/501'/0'/0'"); // Phantom-style import
byte[] signature = keypair.Sign(message);
bool ok = keypair.PublicKey.Verify(message, signature);
SolSharp.Programs:
- Instruction builders:
SystemProgram(transfer, create / allocate / assign — plusCreateAccountWithSeed,AllocateWithSeed,AssignWithSeed,TransferWithSeed— and the durable-nonce set, including theCreateNonceAccountpair),ComputeBudgetProgram(compute-unit limit, priority fee,RequestHeapFrame,SetLoadedAccountsDataSizeLimit),TokenProgram(transfer, mint, burn, approve — checked variants included — revoke,SetAuthorityviaAuthorityType, freeze / thaw, initialize mint / account, close, sync-native — each with atokenProgramoverride for Token-2022),AssociatedTokenAccount(create andCreateIdempotent),AddressLookupTableProgram(create / extend / deactivate / close), andMemoProgram. ProgramDerivedAddress(FindProgramAddress/TryCreateProgramAddress) andPublicKey.IsOnCurve().Message(legacy) andMessageV0(versioned, loading extra accounts from address lookup tables),Transaction, andTransactionBuilder(Build/BuildV0, durable-nonce anchoring viaSetDurableNonce) — compilation, wire serialization (allocation-free viaTransaction.TrySerializeand the spanSerializeoverloads, withTransaction.Deserializeto parse one back, andDecompileInstructionsto resolve a parsed message's instructions to program ids and account keys, loading v0 lookup-table accounts), signing, and base64 output. Every encoding is checked byte-for-byte against the Rustsolana-sdk(via solders) andsolana-py.
using SolSharp.Programs;
using SolSharp.Wallet;
using var payer = Keypair.Parse(secret);
var blockhash = (await rpc.GetLatestBlockhashAsync()).Blockhash;
var tx = new TransactionBuilder()
.SetRecentBlockhash(blockhash)
.AddInstruction(ComputeBudgetProgram.SetComputeUnitPrice(50_000))
.AddInstruction(SystemProgram.Transfer(payer.PublicKey, recipient, 1_000_000))
.Build(payer);
var signature = await rpc.SendTransactionAsync(tx.Serialize());
Roadmap
- Core primitives —
PublicKey,Base58,ShortVec - RPC enum + JSON converters (
Commitment) - Program / sysvar / mint constants + validation
-
SolSharp.Wallet— Ed25519 keys, signing/verification, key parsing -
SolSharp.Rpc— HTTP reads (getAccountInfo/getMultipleAccounts/getProgramAccounts/getSignaturesForAddress, balances, blockhash, token supply, ...) +sendTransaction/simulateTransaction; multiplexed WebSocket streaming (slots, logs, accounts, programs, signatures, blocks) with auto-reconnect and optionalILoggerdiagnostics; DI + resilience -
SolSharp.Programs— System / Token (+ Token-2022) / ATA / Compute Budget / Memo instructions, PDA/ATA, transaction builder - Versioned (v0) transactions + address lookup tables (compile / sign / fetch + decode / ALT program)
- Borsh reader + writer, typed SPL account state (
Mint/TokenAccount),Transaction.Deserialize+ instruction decompilation, and typedTransactionError - Live integration test suite (configurable RPC / WS endpoint)
- Published NuGet package (single
SolSharppackage bundling the four assemblies) - Durable nonces — nonce instructions +
NonceAccountdecoding +TransactionBuilder.SetDurableNonce - Mnemonic wallet import — BIP-39 + SLIP-0010 (
solana-keygenand Phantom/Solflare schemes) - Token-2022 extensions — TLV decoding with typed views (transfer fee, metadata, delegate, ...)
- Devnet write gate — live airdrop / transfer / durable-nonce run before every release
- JSON-RPC batching, allocation-free (span) transaction serialization,
AddSolanaWsDI - BenchmarkDotNet harness (
benchmarks/)
Requirements
- .NET 8 SDK or later.
Build & test
dotnet build
dotnet test
dotnet format # apply the enforced code style
The suite includes a SolSharp.IntegrationTests project that exercises the read and streaming paths against a
live cluster, plus a write suite (airdrop, transfer, durable nonce) that always targets devnet. Reads
default to the public mainnet endpoint (SOLSHARP_RPC_URL / SOLSHARP_WS_URL override); the write suite uses
the public devnet endpoint (SOLSHARP_DEVNET_RPC_URL override); no credentials are committed. These tests hit
the network, so they tolerate rate limits by reporting inconclusive rather than failing, and are tagged
Integration. For a fast, offline-only run, exclude them:
dotnet test --filter "TestCategory!=Integration"
Micro-benchmarks live in a standalone BenchmarkDotNet harness:
dotnet run -c Release --project benchmarks/SolSharp.Benchmarks
To point the integration tests at your own node, set the endpoints (the key stays in your shell, never the repo):
SOLSHARP_RPC_URL=https://your-node SOLSHARP_WS_URL=wss://your-node \
dotnet test --filter "TestCategory=Integration"
Layout
SolSharp/
src/SolSharp.Core/ Encoding/ Primitives/ Converters/ Constants/
src/SolSharp.Rpc/ Protocol/ Models/ Streaming/ + client, options, DI
src/SolSharp.Wallet/ Keypair (+ parsing), ISigner, PublicKey.Verify / IsOnCurve
src/SolSharp.Programs/ instructions, PDA/ATA, Message/Transaction, TransactionBuilder
src/SolSharp/ packaging facade — bundles the four assemblies into the single NuGet package
tests/ NUnit + FluentAssertions, mirroring each project
(+ SolSharp.IntegrationTests: live-cluster read/streaming checks)
.github/workflows/ ci.yml (build + offline tests) and release.yml (tag → NuGet trusted publishing)
assets/ package icon and README logo
.editorconfig modern C# style, enforced on build
Directory.Build.props
CLAUDE.md conventions and decisions for contributors/agents
docs/USAGE.md task-oriented usage guide with runnable examples
Design notes
Coreis dependency-light and free of I/O and crypto by design — anything that needs the network or Ed25519 lives in a higher layer.- Wire formats and signing are money-critical: they are validated against known-good vectors, not just round-trips.
- Conventions, layering rules, and design decisions are documented in
CLAUDE.md.
Security
SolSharp handles private keys and builds transactions that move funds. It has not been audited — use at your own risk. Never commit secrets or private keys, and never hand a raw private key to a dependency you do not control: build with a third-party library if you must, but sign with your own signer and simulate before sending.
License
MIT © Yevhen Koval
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- BouncyCastle.Cryptography (>= 2.6.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Http.Resilience (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options.DataAnnotations (>= 8.0.0)
- SimpleBase (>= 5.6.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
0.6.0 adds Token-2022 extension decoding (TokenExtensionSet: TLV walking with typed views for transfer fees, metadata pointer and in-mint metadata, permanent delegate, and more), JSON-RPC batching (SolanaRpcClient.CreateBatch - reads and sends in one round-trip), allocation-free transaction serialization (Transaction.TrySerialize and span Serialize overloads with GetSerializedLength), and AddSolanaWs DI registration. The release gate now also runs a live devnet write suite (airdrop, transfer, durable-nonce lifecycle) before publishing. Follows semantic versioning; while on 0.x, minor releases may carry breaking changes. Changelog: https://github.com/jecacs/SolSharp/blob/main/CHANGELOG.md