Autonomi.Antd
0.2.0
dotnet add package Autonomi.Antd --version 0.2.0
NuGet\Install-Package Autonomi.Antd -Version 0.2.0
<PackageReference Include="Autonomi.Antd" Version="0.2.0" />
<PackageVersion Include="Autonomi.Antd" Version="0.2.0" />
<PackageReference Include="Autonomi.Antd" />
paket add Autonomi.Antd --version 0.2.0
#r "nuget: Autonomi.Antd, 0.2.0"
#:package Autonomi.Antd@0.2.0
#addin nuget:?package=Autonomi.Antd&version=0.2.0
#tool nuget:?package=Autonomi.Antd&version=0.2.0
antd-csharp — C# SDK for Autonomi
C# SDK for the antd daemon. Provides an async client with both REST and gRPC transports targeting .NET 8.
Installation
dotnet add package Autonomi.Antd
Compatibility
This package talks to a running antd daemon; it does not join the network itself. Targets .NET 8. Tested against antd 0.12.x. The gRPC transport uses Grpc.Net.Client, which is a package dependency, so both transports work out of the box. For a daemon-less client see Autonomi.Ffi.
Prerequisites
- .NET 8 SDK
- antd daemon running (see root README)
Building
cd antd-csharp
# Build all projects (SDK, examples, tests)
dotnet build Antd.sln
# Or build individual projects
dotnet build Antd.Sdk/Antd.Sdk.csproj
Quick Start
using System.Text;
using Antd.Sdk;
using var client = AntdClient.CreateRest();
// Health check
var status = await client.HealthAsync();
Console.WriteLine($"{status.Network} — healthy: {status.Ok}");
// Store and retrieve data
var result = await client.DataPutPublicAsync(
Encoding.UTF8.GetBytes("Hello, Autonomi!")
);
Console.WriteLine($"Address: {result.Address}, chunks: {result.ChunksStored}");
var data = await client.DataGetPublicAsync(result.Address);
Console.WriteLine(Encoding.UTF8.GetString(data));
Client Creation
using Antd.Sdk;
// REST transport (default)
using var client = AntdClient.CreateRest(
baseUrl: "http://localhost:8082",
timeout: TimeSpan.FromSeconds(30)
);
// gRPC transport (wallet operations and payment_mode are REST-only)
using var grpcClient = AntdClient.CreateGrpc(
target: "http://localhost:50051"
);
// Factory method (transport string)
using var auto = AntdClient.Create(transport: "rest");
API Reference
All methods are async and return Task<T>. The client implements IDisposable.
Health
| Method | Returns | Description |
|---|---|---|
HealthAsync() |
HealthStatus |
Check daemon health — also reports antd version, EVM network, uptime, build commit, and payment contract addresses (antd ≥ 0.4.0) |
Data
| Method | Returns | Description |
|---|---|---|
DataPutPublicAsync(byte[] data, PaymentMode mode = Auto) |
DataPutPublicResult |
Store public data — DataMap stored on-network |
DataGetPublicAsync(string address) |
byte[] |
Retrieve public data by address |
DataPutAsync(byte[] data, PaymentMode mode = Auto) |
DataPutResult |
Store private (encrypted) data — DataMap returned to caller |
DataGetAsync(string dataMap) |
byte[] |
Retrieve private data using a caller-held DataMap |
DataCostAsync(byte[] data, PaymentMode mode = Auto) |
UploadCostEstimate |
Estimate storage cost — size, chunks, gas, payment mode |
Chunks
| Method | Returns | Description |
|---|---|---|
ChunkPutAsync(byte[] data) |
PutResult |
Store a raw chunk |
ChunkGetAsync(string address) |
byte[] |
Retrieve a chunk |
Files
| Method | Returns | Description |
|---|---|---|
FilePutAsync(string path, PaymentMode mode = Auto) |
FilePutResult |
Upload a file privately — DataMap returned to caller |
FileGetAsync(string dataMap, string destPath) |
— | Download a private file using a caller-held DataMap |
FilePutPublicAsync(string path, PaymentMode mode = Auto) |
FilePutPublicResult |
Upload a file publicly — DataMap stored on-network |
FileGetPublicAsync(string address, string destPath) |
— | Download a public file by address |
FileCostAsync(string path, bool isPublic, PaymentMode mode = Auto) |
UploadCostEstimate |
Estimate cost — size, chunks, gas, payment mode |
Models
All models are sealed records (immutable).
| Model | Fields | Description |
|---|---|---|
HealthStatus |
Ok, Network, Version, EvmNetwork, UptimeSeconds, BuildCommit, PaymentTokenAddress, PaymentVaultAddress |
Health check result (diagnostic fields require antd ≥ 0.4.0) |
PutResult |
Cost, Address |
Result of ChunkPutAsync only |
DataPutResult |
DataMap, ChunksStored, PaymentModeUsed |
Private data put — DataMap returned to caller |
DataPutPublicResult |
Address, ChunksStored, PaymentModeUsed |
Public data put — DataMap stored on-network |
FilePutResult |
DataMap, StorageCostAtto, GasCostWei, ChunksStored, PaymentModeUsed |
Private file put — DataMap returned to caller |
FilePutPublicResult |
Address, StorageCostAtto, GasCostWei, ChunksStored, PaymentModeUsed |
Public file put — DataMap stored on-network |
UploadCostEstimate |
Cost, FileSize, ChunkCount, EstimatedGasCostWei, PaymentMode |
Pre-upload cost breakdown |
Error Handling
All errors inherit from AntdException:
using Antd.Sdk;
try
{
var data = await client.DataGetPublicAsync("nonexistent");
}
catch (NotFoundException)
{
Console.WriteLine("Data not found");
}
catch (PaymentException)
{
Console.WriteLine("Insufficient funds");
}
catch (AntdException ex)
{
Console.WriteLine($"Error ({ex.StatusCode}): {ex.Message}");
}
| Exception | HTTP | gRPC | Description |
|---|---|---|---|
BadRequestException |
400 | INVALID_ARGUMENT |
Invalid parameters |
PaymentException |
402 | FAILED_PRECONDITION |
Payment issue |
NotFoundException |
404 | NOT_FOUND |
Not found |
AlreadyExistsException |
409 | ALREADY_EXISTS |
Already exists |
ForkException |
409 | ABORTED (non-partial-upload) |
Version conflict |
TooLargeException |
413 | RESOURCE_EXHAUSTED |
Too large |
InternalException |
500 | INTERNAL |
Server error |
NetworkException |
502 | UNAVAILABLE |
Unreachable |
PartialUploadException |
502 (code: "PARTIAL_UPLOAD") |
ABORTED (detail starts with Partial upload:) |
Finalize stored some chunks, not all; extends NetworkException |
Partial uploads
A finalize (FinalizeUploadAsync, FinalizeMerkleUploadAsync, FinalizeChunkUploadAsync) can fail after the wallet has paid: some chunks store, others miss quorum after the daemon's own retries. The SDK surfaces that as PartialUploadException with ChunksStored, ChunksFailed, TotalChunks, Retryable and RetentionKnown. The on-chain payment persists and the stored chunks stay on the network; the two flags say how to finish:
Retryable: the daemon kept the paid attempt (payment proofs plus the unstored chunks) under the sameupload_id. Call the same finalize method again with the same arguments (sameupload_id, same payment artefacts) to store the remainder against the same payment: no re-prepare, no second signature, no double payment. Bound the loop: a persistent failure throws on every call, so cap the attempts and treat aChunksFailedthat stops shrinking as stuck. The retained attempt expires with the daemon's pending-upload TTL.RetentionKnown && !Retryable: the daemon confirmed it kept nothing (for example a merkle finalize that deliberately left sub-batches unpaid). Re-prepare the same content; already-stored chunks are skipped, so the retry pays only for the remainder.!RetentionKnown: retention is unknown, because a daemon before antd 0.14.0 answered over REST (it never sendsretryable) or the SDK could not fully read the error. The daemon may still hold the paid attempt, since it records the resume handle before it returns the error. Stop automatic recovery, keep theupload_idand the original payment artefacts, and reconcile before re-preparing or paying again. Never pay again on this signal alone.
Retryable implies RetentionKnown. PartialUploadException extends NetworkException because a partial upload has always arrived as a 502, so existing catch (NetworkException) blocks keep matching; catch the derived type first to branch on the counts. Over REST the counts come from the structured error body, and RetentionKnown is true only when retryable is a JSON boolean (missing, null, "true" or 1 read as unknown). Over gRPC an ABORTED status maps to PartialUploadException only when its status detail starts with the daemon's fixed Partial upload: prefix; any other ABORTED, including one that quotes Partial upload: further into its detail, keeps the ForkException mapping. The detail reads Partial upload: <stored>/<total> chunks stored, <failed> failed after retries: <reason> (<hint>). RetentionKnown is true only when the detail starts with those counts, all three parse, and it ends with one of the daemon's two hints: (paid attempt retained...) sets Retryable, and (stored chunks persist; re-prepare the same content...) means the daemon confirmed it kept nothing (daemons before antd 0.14.0 write only this one). If the counts do not match the daemon's layout or do not convert (an overflow, non-ASCII digits), all three read as zero and both flags are false, even with a hint. Readable counts with a missing, truncated or unrecognised hint, or text after it, keep the counts, but both flags stay false: retention is unknown (stop and reconcile), not "nothing retained".
Behaviour change over gRPC: a partial-upload ABORTED used to surface as ForkException; it is now PartialUploadException. The daemon emits ABORTED only for PARTIAL_UPLOAD, so a catch (ForkException) around a gRPC finalize should become catch (PartialUploadException).
var lastFailed = 0UL;
for (var attempt = 1; ; attempt++)
{
try
{
return await client.FinalizeUploadAsync(uploadId, txHashes); // every chunk stored
}
catch (PartialUploadException ex) when (ex.Retryable)
{
var stalled = attempt > 1 && ex.ChunksFailed >= lastFailed;
if (attempt >= 5 || stalled)
throw; // still retained under uploadId until the daemon's TTL: finalize again later
lastFailed = ex.ChunksFailed;
await Task.Delay(TimeSpan.FromSeconds(attempt * 2), cancellationToken);
}
// RetentionKnown && !Retryable propagates: the daemon kept nothing, so re-prepare the same content.
// !RetentionKnown propagates: retention unknown, so keep uploadId + txHashes and reconcile before paying again.
}
See Examples/FinalizeRetry.cs (FinalizeWithRetryAsync, used by example 7) and docs/external-signer-flow.md §6.
Examples
cd Examples
dotnet run -- 1 # Connect
dotnet run -- 2 # Public data
dotnet run -- 3 # Chunks
dotnet run -- 4 # Files
dotnet run -- 6 # Private data
dotnet run -- 7 # External signer (bounded partial-upload retry)
dotnet run -- all # Run all
Or use the dev CLI:
ant dev example data -l csharp
ant dev example all -l csharp
Project Structure
antd-csharp/
├── Antd.sln # Solution file
├── Antd.Sdk/ # SDK library
│ ├── Antd.Sdk.csproj
│ ├── IAntdClient.cs # Client interface
│ ├── AntdClientFactory.cs # Factory methods
│ ├── AntdRestClient.cs # REST implementation
│ ├── AntdGrpcClient.cs # gRPC implementation
│ ├── Models.cs # Data models
│ └── Exceptions.cs # Exception hierarchy
├── Examples/ # Example programs
│ ├── Examples.csproj
│ └── Program.cs
└── Antd.Sdk.Tests/ # Tests
├── Antd.Sdk.Tests.csproj
└── Program.cs
| 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
- Google.Protobuf (>= 3.34.0)
- Grpc.Net.Client (>= 2.76.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.