A .NET client library for the CSPR Cloud API — access Casper blockchain data (Mainnet & Testnet) with type-safe methods, filtering, sorting, pagination, and a WebSocket Streaming API.
Catches the SDK up with the CSPR Cloud API through v2.9.0 (2026-02). Covers every changelog entry from v2.0.3 → v2.9.0 and includes Casper 2.0 fields across Deploy / Validator / Bidder / Block / Supply.
- Balance and stake fields migrated from
ulong?tostringto match the v2.4.3 wire format and avoid uint64 overflow on large accounts.AccountData:Balance,DelegatedBalance,UndelegatedBalance,StakedBalance,UndelegatingBalanceValidatorData:SelfStake,DelegatorsStake,TotalStake,SelfShare,NetworkShare(plus newBidAmount,MinimumDelegationAmount,MaximumDelegationAmount,PendingUnstakingAmount)BidderData: same set as ValidatorAuctionMetricsData.TotalActiveEraStakeValidatorRewardData.Amount
SupplyData.Timestampmigrated fromDateTime?tolong?(Unix seconds — the endpoint emits it as a number, not ISO-8601).- Callers comparing these fields must parse with
BigInteger.Parse/decimal.Parse/long.
New endpoints
// Account undelegations (v2.5.1) — pending funds in the 7-era unbonding window
var pending = await client.Testnet.Delegate.GetAccountUndelegationsAsync("01publicKey...");
// Returns PaginatedResponse<UndelegationData> with DelegatorIdentifier + DelegatorIdentifierTypeId
// Unscoped NFT listing with filtering across the whole network
var nfts = await client.Testnet.NFT.GetNFTsAsync(new NFTsRequestParameters
{
FilterParameters = new NFTsFilterParameters
{
ContractPackageHash = "pkg1...",
OwnerHash = "hash1..."
}
});New filters
DeploysFilterParameters.CallerHash(v2.0.12)ft_action_type_idon all three FT-action filter classes (v2.0.20)nft_action_idon both NFT-action filter classes (v2.0.20)owner_hashonNFTContractPackageFilterParameters(v2.0.20)from_era_id/to_era_idonAccountDelegatorRewardFilterParametersand the newValidatorRewardsFilterParameters(v2.4.0) — wired into bothGetValidatorRewardsAsyncandGetValidatorEraRewardsAsynccontract_package_hashon the newFTAccountOwnershipFilterParameters(v2.6.0)GetAccountNFTActionsAsyncURL builder now threadsFilterParameters(was previously unwired)
New includers
ContractPackageOptionalParameters:TokenMarketData(function includer, takes a currency id),CoingeckoData,FriendlymarketData,CsprtradeData,OwnerCsprName- CSPR.name sweep across Account, Validator, Bidder, Delegation, DelegatorReward, Deploy caller, Transfer, Block proposer, FT/NFT actions & ownership
AccountsOptionalParametersgainsRank
New response properties
DeployData(Casper 2.0):CallerHash,VersionId,PricingModeId,GasPriceLimit,IsStandardPayment,RuntimeTypeId,ConsumedGas,RefundAmount,CallerCsprNameValidatorData/BidderData:BidAmount,ReservedSlots, min/max delegation amounts,PendingUnstakingAmount,EraId,DelegatorsNumber,DelegatorsStake,CsprNameSupplyData:TotalAnnualIssuance,AnnualEcosystemSustainIssuance,AnnualStakingRewardsIssuance,AnnualIssuanceContractPackageData:WebsiteUrl,OwnerHash,CoingeckoId,IsContractInfoApproved,IsFeatured,OwnerCsprName, plus rawJObjectpayloads for the four market-data includersBlockData: Casper 2.0 transaction buckets (AuctionTxnNumber,InstallUpgradeTxnNumber,SmallTxnNumber,MediumTxnNumber,LargeTxnNumber),GasPrice,VersionId,ProposerCentralizedAccountInfo,ProposerCsprNameTransferData:TransferIndex+ four CSPR.name fields (initiator / to / from-purse / to-purse)DelegationData/DelegatorRewardData:DelegatorIdentifier+DelegatorIdentifierTypeId(v2.1.0 purse delegation support), plus CSPR.name fieldsValidatorRewardData:Type(Casper 2.0 RewardTypeID) and string-typedAmount
Streaming
- Contracts streaming channel (
.Contract) verified againststreaming.testnet.cspr.cloud/contractsand unskipped.
Internal
- Added
CSPRCloudNetRestUrlTests.cswith 40 fast unit tests covering URL construction + deserialization of every new field (sub-100ms run time). - Full surface audit saved to
docs/audit-2026-04-22.md.
Fix: the Streaming API pump now tolerates the plaintext Ping keepalive text frames that the CSPR Cloud server interleaves between JSON envelopes. v1.2.0 would terminate the subscription with a JsonReaderException on the first keepalive (~5s after connect). Non-JSON frames are now skipped via a new IsJsonEnvelope guard.
Adds the full Streaming API — WebSocket-based real-time subscriptions — via the new CasperCloudSocketClient.
All 10 streaming channels implemented (Mainnet + Testnet):
- Account balance —
updatedevents - Block —
createdevents - Contract —
createdevents - Contract package —
created,updatedevents - Contract-level events —
emittedevents (with optionalraw_data) - Deploy —
createdevents - Fungible token action —
createdevents - NFT —
created,updatedevents - NFT action —
createdevents - Transfer —
createdevents
Other changes:
- Auto-reconnect with exponential backoff + jitter via opt-in
StreamReconnectPolicy— addresses the CSPR docs' guidance that "WebSocket connections may close during API deployments, necessitating reconnection logic in client applications" Persistent-Sessionheader support for replaying queued messages across reconnects (paid tiers, Beta)INetworkSocketEndpointinterface mirrors the RESTINetworkEndpoint, so the same runtime-switching pattern works for streamsCasperCloudRestClient.Mainnet/Testnetnow typed asINetworkEndpoint— letsvar network = useTestnet ? client.Testnet : client.Mainnet;compile- 50 new tests covering URL construction, envelope deserialization, reconnect policy math, retry/veto/fatal/cancellation paths, and live handshake checks against testnet for every stream
Usage:
var socket = new CasperCloudSocketClient(
new CasperCloudClientConfig("your-api-key"),
reconnectPolicy: new StreamReconnectPolicy { Enabled = true });
using var cts = new CancellationTokenSource();
await socket.Testnet.Block.SubscribeAsync(
parameters: null,
onMessage: msg => { Console.WriteLine(msg.Data.BlockHash); return Task.CompletedTask; },
cancellationToken: cts.Token);Added 20 new API endpoints covering 8 new categories and 6 missing endpoints in existing categories, aligning with CSPR.cloud API v2.9.0.
New Categories:
- DEX - Get list of DEXes
- Swap - Get paginated list of fungible token trades with filtering, sorting, and optional properties
- FT Rate - Get latest and historical fungible token rates by currency
- FT Daily Rate - Get latest and historical daily aggregated token rates
- FT DEX Rate - Get latest and historical token-to-token exchange rates
- FT Daily DEX Rate - Get latest and historical daily token-to-token rates
- CSPR.name Resolution - Resolve CSPR.name to account hash
- Awaiting Deploy - Create, add signatures to, and retrieve awaiting deploys (multisig workflows)
New Endpoints in Existing Categories:
- Purse Transfers - Get transfers by purse URef
- Purse Delegations - Get delegations by purse URef
- Purse Delegation Rewards - Get delegation rewards and totals by purse URef
- Validator Era Rewards - Get validator rewards aggregated by era
- FT Action Types - Get list of fungible token action types
Other Changes:
- Added
PostDataAsyncsupport for POST endpoints - Added 20 new unit tests
Usage Examples:
// Get DEXes
var dexes = await restClient.Testnet.Dex.GetDexesAsync();
// Get swaps with filtering
var swapParams = new SwapRequestParameters { PageSize = 25 };
var swaps = await restClient.Testnet.Swap.GetSwapsAsync(swapParams);
// Get latest FT rate
var filterParams = new FTRateFilterParameters { CurrencyId = "1" };
var rate = await restClient.Testnet.FT.GetFTRateLatestAsync("contract-package-hash", filterParams);
// Get historical FT daily rates
var dailyParams = new FTDailyRateRequestParameters();
dailyParams.FilterParameters.CurrencyId = "1";
var dailyRates = await restClient.Testnet.FT.GetFTDailyRatesAsync("contract-package-hash", dailyParams);
// Resolve CSPR.name
var resolution = await restClient.Testnet.CsprName.GetCsprNameResolutionAsync("cloud.cspr");
// Get validator era rewards
var eraRewards = await restClient.Testnet.Validator.GetValidatorEraRewardsAsync("validator-public-key");
// Get purse transfers
var purseTransfers = await restClient.Testnet.Transfer.GetPurseTransfersAsync("uref-...-007");- Added
era_idandis_switch_blockfilter parameters for theGetBlocksendpoint - You can now filter blocks by era and switch block status:
var parameters = new BlockRequestParameters
{
FilterParameters = new BlockFilterParameters
{
EraId = "21180",
IsSwitchBlock = true
}
};
var result = await restClient.Testnet.Block.GetBlocksAsync(parameters);- Added
INetworkEndpointinterface allowing easy switching between Mainnet and Testnet endpoints with type safety
- Enhanced
GetContractPackageFungibleTokenOwnershipendpoint with sorting by balance and optional parameters
- Added
contract_package_hashandaccount_hashfilters toFTAccountActionFilterParameters - Enhanced fungible token action filtering capabilities for
GetAccountFTActionsAsyncmethods on both Mainnet and Testnet
- Update tests and client for new validator performance data
Updated CSPRCloudNetTests.cs:
- Added assertion to check
Scorevalues are not null. - Replaced
AverageScoreordering checks withScoreordering checks.
Updated CasperCloudRestClient.cs:
- Changed return type of
GetHistoricalValidatorsAveragePerformanceAsynctoPaginatedResponse<RelativeValidatorPerformanceData>. - Updated endpoint call, method signature, and documentation to reflect new return type.
These changes ensure compatibility with updated data structures and API responses, improving the accuracy and reliability of tests and client methods.
- Fixed mainnet baseUrl
- Fixed an issue where mainnet endpoints were using testnet baseurl.
- Changes:
- GetAccountInfo endpoint replaced with GetAccountInfoAsync
- Initial Release
The library is targeting both .NET Standard 2.0 and .NET Standard 2.1 for optimal compatibility
Package Manager
NuGet\Install-Package CSPR.Cloud.Net
.NET CLI
dotnet add package CSPR.Cloud.Net
The CasperCloudRestClient class provides an easy way to interact with the CSPR Cloud API for both Mainnet and Testnet environments. Below are the steps to initialize and use the client in your application.
To create an instance of the CasperCloudRestClient, you need to provide your API key. Optionally, you can also pass a custom HttpClient and ILoggerFactory for logging purposes.
Ensure you have the necessary dependencies in your project. Typically, this includes:
HttpClientILoggerFactoryfrom Microsoft.Extensions.Logging
Create an instance of the CasperCloudClientConfig class with your API key.
public class CasperCloudClientConfig
{
public string ApiKey { get; set; }
public CasperCloudClientConfig(string apiKey)
{
if (string.IsNullOrEmpty(apiKey))
throw new ArgumentException("API key is required.", nameof(apiKey));
ApiKey = apiKey;
}
}
Initialize the CasperCloudRestClient with your configuration, and optionally pass in a custom HttpClient and ILoggerFactory.
using System.Net.Http;
using Microsoft.Extensions.Logging;
// Configuration with API key
var config = new CasperCloudClientConfig("your-api-key");
// Optional: Custom HttpClient
HttpClient customHttpClient = new HttpClient();
// Optional: LoggerFactory for logging
ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
// Create the CasperCloudRestClient
var restClient = new CasperCloudRestClient(config, customHttpClient, loggerFactory);
Use the Mainnet and Testnet properties to access different endpoints. For example:
// Accessing the Account endpoint on Testnet
var accountData = await restClient.Testnet.Account.GetAccountAsync("public-key");
// Accessing the Block endpoint on Mainnet
var blockData = await restClient.Mainnet.Block.GetBlockAsync("block-hash");
Both Mainnet and Testnet implement the INetworkEndpoint interface, allowing you to easily switch between networks:
// Choose network based on configuration
bool useTestnet = true; // or from configuration
INetworkEndpoint network = useTestnet ? restClient.Testnet : restClient.Mainnet;
// Use the selected network throughout your application
var account = await network.Account.GetAccountAsync("public-key");
var block = await network.Block.GetBlockAsync("block-hash");This is useful when you want to:
- Switch networks based on environment (development/production)
- Pass the network endpoint to services without coupling them to a specific network
- Write reusable code that works with both networks
"CsprCloud": {
"ApiKey": "your-api-key"
}
context.Services.AddSingleton(resolver =>
{
var apiKey = configuration.GetSection("CsprCloud:ApiKey").Value;
return new CasperCloudRestClient(new CasperCloudClientConfig(apiKey));
});
public class IndexModel : PageModel
{
private readonly CasperCloudRestClient _restClient;
public IndexModel(CasperCloudRestClient restClient)
{
_restClient = restClient;
}
public async Task OnGet()
{
var result = await _restClient.Mainnet.Account.GetAccountInfoAsync("bb436216f3f56b073fc712c024a01c1291292e9533a03ddabc67ef85360b00bf");
}
}
Use CasperCloudSocketClient for real-time subscriptions. The shape mirrors the REST client — pick Mainnet or Testnet, pick a stream, call SubscribeAsync with optional filters and a message handler.
using CSPR.Cloud.Net.Clients;
using CSPR.Cloud.Net.Objects.Config;
var socket = new CasperCloudSocketClient(new CasperCloudClientConfig("your-api-key"));
using var cts = new CancellationTokenSource();
await socket.Testnet.Block.SubscribeAsync(
parameters: null,
onMessage: msg =>
{
Console.WriteLine($"{msg.Action} @ {msg.Data.BlockHash}");
return Task.CompletedTask;
},
cancellationToken: cts.Token);Every stream lives under socket.Mainnet.{Stream} and socket.Testnet.{Stream}:
| Stream | Actions | Payload type |
|---|---|---|
.AccountBalance |
updated |
AccountBalanceStreamData |
.Block |
created |
BlockData |
.Contract |
created |
ContractData |
.ContractPackage |
created, updated |
ContractPackageData |
.ContractEvent |
emitted |
ContractEventStreamData |
.Deploy |
created |
DeployData |
.FTTokenAction |
created |
FTTokenActionData |
.NFT |
created, updated |
NFTTokenData |
.NFTAction |
created |
NFTTokenActionData |
.Transfer |
created |
TransferData |
Every message is delivered as a WebSocketMessage<T> envelope with Action, Data, Extra (per-stream JObject), and Timestamp.
Each stream has a parameters class exposing its server-side filters as List<string> (comma-joined in the URL). Leave null to skip a filter:
await socket.Testnet.Deploy.SubscribeAsync(
new DeployStreamParameters
{
CallerPublicKey = new List<string> { "01abc..." },
ContractPackageHash = new List<string> { "pkg1..." }
},
onMessage: msg => { Handle(msg); return Task.CompletedTask; },
cancellationToken: ct);The ContractEvent stream requires exactly one of ContractHash or ContractPackageHash — the client throws ArgumentException on BuildUri if neither is supplied, so you fail fast instead of hitting a 400.
Mainnet and Testnet both return INetworkSocketEndpoint, so the same pattern you use for REST works:
bool useTestnet = true;
var network = useTestnet ? socket.Testnet : socket.Mainnet;
await network.Transfer.SubscribeAsync(parameters: null, onMessage, cancellationToken: ct);WebSocket connections may drop during CSPR Cloud deployments. Enable the built-in reconnect policy to survive those transparently:
var socket = new CasperCloudSocketClient(
new CasperCloudClientConfig("your-api-key"),
persistentSessionId: "my-consumer-id", // optional; replays queued messages across reconnects (Beta, paid tiers)
reconnectPolicy: new StreamReconnectPolicy
{
Enabled = true,
InitialDelay = TimeSpan.FromSeconds(1),
MaxDelay = TimeSpan.FromSeconds(60),
BackoffMultiplier = 2.0,
JitterFactor = 0.25,
MaxRetries = -1 // -1 = retry forever
});
await socket.Testnet.Block.SubscribeAsync(
parameters: null,
onMessage: msg => { Handle(msg); return Task.CompletedTask; },
onError: ex => { Log(ex); return Task.CompletedTask; }, // parse errors (envelope keeps flowing)
onReconnecting: (ex, attempt) =>
{
Console.WriteLine($"reconnecting (attempt {attempt}): {ex?.Message}");
return Task.CompletedTask;
},
cancellationToken: ct);The default retry gate retries transport exceptions (WebSocketException, IOException, TimeoutException) and clean server disconnects; it refuses ArgumentException / InvalidOperationException. Override via ShouldReconnect = (ex, attempt) => ... for custom rules.
SubscribeAsync returns a Task that only completes when you cancel the CancellationToken (or, if reconnect is off, when the server closes the socket). Just cancel the CTS:
cts.Cancel(); // the subscription task exits with OperationCanceledExceptionMost of the endpoints require optional request parameters wrapped in a RequestParameters class, which includes three possible components:
- FilterParameters: Used to filter the results.
- SortingParameters: Used to sort the results.
- OptionalParameters: Used to include additional data in the results.
Parameters and properties differ for each request depending on the endpoint.
Here's an example of how to initialize and use parameterized requests for the Validators endpoint:
// Initialize the request parameters
var parameters = new ValidatorsRequestParameters
{
// Result is filtered for EraId = 14027 and IsActive = true
FilterParameters = new ValidatorsFilterParameters
{
EraId = "14027",
IsActive = true
},
// Result will include AccountInfo, CentralizedAccountInfo and AveragePerformance Entities
OptionalParameters = new ValidatorsOptionalParameters
{
AccountInfo = true,
CentralizedAccountInfo = true,
AveragePerformance = true,
},
// Result is sorted by Total Stake because it is set to true and the Sort Direction is Descending
SortingParameters = new ValidatorsSortingParameters
{
OrderByTotalStake = true,
SortType = SortType.Descending
}
};
// Execute the request
var result = await _restClient.Testnet.Validator.GetValidatorsAsync(parameters);
Below are some examples of demonstrating how to use the CasperCloudRestClient.
For more detailed examples of all endpoints, please refer to the CSPRCloudNetTests.cs file.
var config = new CasperCloudClientConfig("your-api-key");
_restClient = new CasperCloudRestClient(config);
// Without parameters
var result = await _restClient.Testnet.Account.GetAccountAsync(_testPublicKey);
// With parameters
var parameters = new AccountInfosRequestParameters
{
FilterParameters = new AccountInfosFilterParameters
{
AccountHashes = new List<string>
{
"first-account-hash",
"second-account-hash"
}
}
};
var result = await _restClient.Testnet.Account.GetAccountInfosAsync(parameters);
Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request.
MIT License
BTC: 1NxUuEQcR4Scw8ge3oto6ykLqBpe9LGikS
ETH: 0x9cda155f73220073a9f024daaa72eb06b5c06c86
CSPR Public Key: 01a0cbbd2f6402c98c745b6d318d15c0b68feef6a17af48ae35e683f05a4e6cbcc